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