1 /*
2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation.  The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301  USA
24 */
25 
26 #include "ngs/protocol/message_builder.h"
27 #include "ngs/protocol/output_buffer.h"
28 
29 #include "ngs_common/protocol_protobuf.h"
30 
31 using namespace ngs;
32 
33 
Message_builder()34 Message_builder::Message_builder()
35 : m_out_buffer(NULL)
36 {}
37 
~Message_builder()38 Message_builder::~Message_builder()
39 {
40 }
41 
encode_uint32(uint32 value,bool write)42 void Message_builder::encode_uint32(uint32 value, bool write)
43 {
44   ++m_field_number;
45   if (write)
46   {
47     google::protobuf::internal::WireFormatLite::WriteTag(m_field_number, google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT, m_out_stream.get());
48     m_out_stream->WriteVarint32(value);
49   }
50 }
51 
encode_uint64(uint64 value,bool write)52 void Message_builder::encode_uint64(uint64 value, bool write)
53 {
54   ++m_field_number;
55   if (write)
56   {
57     google::protobuf::internal::WireFormatLite::WriteTag(m_field_number, google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT, m_out_stream.get());
58     m_out_stream->WriteVarint64(value);
59   }
60 }
61 
encode_int32(int32 value,bool write)62 void Message_builder::encode_int32(int32 value, bool write)
63 {
64   ++m_field_number;
65   if (write)
66   {
67     google::protobuf::internal::WireFormatLite::WriteTag(m_field_number, google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT, m_out_stream.get());
68     m_out_stream->WriteVarint32SignExtended(value);
69   }
70 }
71 
encode_string(const char * value,size_t len,bool write)72 void Message_builder::encode_string(const char* value, size_t len, bool write)
73 {
74   ++m_field_number;
75   if (write)
76   {
77     google::protobuf::internal::WireFormatLite::WriteTag(m_field_number, google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, m_out_stream.get());
78     m_out_stream->WriteVarint32(static_cast<google::protobuf::uint32>(len));
79     m_out_stream->WriteRaw(value, static_cast<int>(len));
80   }
81 }
82 
start_message(Output_buffer * out_buffer,uint8 type)83 void Message_builder::start_message(Output_buffer* out_buffer, uint8 type)
84 {
85   m_field_number = 0;
86 
87   m_out_buffer = out_buffer;
88   m_out_buffer->save_state();
89   m_out_buffer->reserve(5);
90   m_start_from = static_cast<uint32>(m_out_buffer->ByteCount());
91   m_out_stream.reset(ngs::allocate_object<CodedOutputStream>(m_out_buffer));
92 
93   // at this point we don't know the size but we need to reserve the space for it
94   // it is possible that the size which is stored on 4-bytes will be split into 2 pages
95   // in that case we need to keep 2 addresses to be able to write the size when it is known
96   m_out_stream->GetDirectBufferPointer(reinterpret_cast<void**>(&m_size_addr1), &m_size_addr1_size);
97   assert(m_size_addr1_size >= 1);
98   if (static_cast<size_t>(m_size_addr1_size) < sizeof(google::protobuf::uint32))
99   {
100     int bytes_left = sizeof(google::protobuf::uint32) - m_size_addr1_size;
101     int size_addr2_size;
102     m_out_stream->Skip(m_size_addr1_size);
103     m_out_stream->GetDirectBufferPointer(reinterpret_cast<void**>(&m_size_addr2), &size_addr2_size);
104     assert(size_addr2_size > bytes_left);
105     m_out_stream->Skip(bytes_left);
106   }
107   else
108   {
109     m_size_addr1_size = sizeof(google::protobuf::uint32);
110     m_out_stream->Skip(m_size_addr1_size);
111   }
112 
113   // write message type
114   m_out_stream->WriteRaw(&type, 1);
115 }
116 
end_message()117 void Message_builder::end_message()
118 {
119   // here we already know the buffer size, so write it at the beginning of the buffer
120   // the order is important here as the stream's destructor calls buffer's BackUp() validating ByteCount
121   m_out_stream.reset();
122 
123   uint32 msg_size = static_cast<uint32>(m_out_buffer->ByteCount()) - m_start_from - sizeof(google::protobuf::uint32);
124   if (static_cast<size_t>(m_size_addr1_size) >= sizeof(google::protobuf::uint32))
125   {
126     // easy case, whole size written into continous memory
127     CodedOutputStream::WriteLittleEndian32ToArray(msg_size, static_cast<google::protobuf::uint8*>(m_size_addr1));
128   }
129   else
130   {
131     // message size is split into 2 pages
132     google::protobuf::uint8 source[4];
133     memcpy(source, &msg_size, sizeof(msg_size));
134 #ifdef WORDS_BIGENDIAN
135     std::swap(source[0], source[3]);
136     std::swap(source[1], source[2]);
137 #endif
138     google::protobuf::uint8* target[4];
139     target[0] = m_size_addr1;
140     target[1] = (m_size_addr1_size > 1) ? (m_size_addr1 + 1) : (m_size_addr2 + 1 - m_size_addr1_size);
141     target[2] = (m_size_addr1_size > 2) ? (m_size_addr1 + 2) : (m_size_addr2 + 2 - m_size_addr1_size);
142     target[3] = m_size_addr2 + 3 - m_size_addr1_size;
143 
144     for (size_t i = 0; i < 4; ++i)
145       *target[i] = source[i];
146   }
147 }
148 
encode_empty_message(Output_buffer * out_buffer,uint8 type)149 void Message_builder::encode_empty_message(Output_buffer* out_buffer, uint8 type)
150 {
151   const uint32 MSG_SIZE = sizeof(uint8);
152   out_buffer->add_int32(MSG_SIZE);
153   out_buffer->add_int8(type);
154 }
155