1 /*
2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
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 02110-1301  USA
23  */
24 
25 #ifndef PLUGIN_X_PROTOCOL_ENCODERS_ENCODING_PROTOBUF_H_
26 #define PLUGIN_X_PROTOCOL_ENCODERS_ENCODING_PROTOBUF_H_
27 
28 #include <google/protobuf/wire_format_lite.h>
29 #include <cassert>
30 #include <cstdint>
31 #include <string>
32 
33 #include "my_dbug.h"
34 
35 #include "plugin/x/protocol/encoders/encoding_primitives.h"
36 
37 namespace protocol {
38 
39 class Delayed_fixed_varuint32 {
40  public:
Delayed_fixed_varuint32()41   Delayed_fixed_varuint32() : m_out(nullptr) {}
Delayed_fixed_varuint32(uint8_t * & out)42   explicit Delayed_fixed_varuint32(uint8_t *&out) : m_out(out) { out += 5; }
43 
encode(const uint32_t value)44   void encode(const uint32_t value) const {
45     DBUG_ASSERT(m_out);
46     uint8_t *out = m_out;
47     primitives::base::Varint_length<5>::encode(out, value);
48   }
49 
50  private:
51   uint8_t *m_out;
52 };
53 
54 /**
55   Class responsible for protobuf message serialization
56 
57   The class is compatible with serializes supplied in libprotobuf and its
58   kept simple to be possible to run ubench on it.
59   The goal is to serialize the message payload into `Encoding_buffer class
60   with the constrain that "encode_*" method doesn't check if buffer sufficient
61   space inside it. Only following functions do that:
62 
63   * encode_field_delimited_raw
64   * encode_field_string
65 
66   the reason is that, those function can serialize large amount of data, and
67   other at most 20 bytes.
68 
69   The user of this class should group together encode calls and check the space
70   before, for example:
71 
72   ```
73      ensure_buffer_size(100);
74 
75      encode_field_bool<10>(true);
76      encode_field_bool<11>(true);
77      encode_field_var_sint32<12>(2238);
78      ...
79   ```
80 
81   User code should develop a method to check if the required size is sufficient
82   to serialize subsequent encode calls.
83 */
84 class Protobuf_encoder : public Primitives_encoder {
85  private:
86   using Helper = primitives::base::Helper;
87 
88  public:
89   /**
90       Check if output buffer has at last `size` bytes available.
91 
92       If the current buffer page has less data the needed,
93       then next page should be acquired.
94 
95       @tparam size   required number of bytes
96     */
97   template <uint32_t size>
ensure_buffer_size()98   void ensure_buffer_size() {
99     // Static methods that check available size always must succeed
100     m_buffer->ensure_buffer_size<size>();
101     m_page = m_buffer->m_current;
102   }
103 
104  public:
Protobuf_encoder(Encoding_buffer * buffer)105   explicit Protobuf_encoder(Encoding_buffer *buffer)
106       : Primitives_encoder(buffer) {}
107 
108   /**
109     Function serializes a bool field.
110 
111     User need to guarantee by calling `ensure_buffer_size`
112     that the buffer has at least 11 bytes (when its not checking
113     how long field entry is going to be generated by `field_id`).
114   */
115   template <uint32_t field_id>
encode_field_bool(const bool value)116   void encode_field_bool(const bool value) {
117     encode_const_var_uint<Helper::encode_field_tag(
118         field_id, Helper::WireType::WIRETYPE_VARINT)>();
119     encode_fixedvar8_uint8(value ? 1 : 0);
120   }
121 
122   /**
123     Function serializes a varint32 field.
124 
125     User need to guarantee by calling `ensure_buffer_size`
126     that the buffer has at least 15 bytes (when its not checking
127     how long field entry is going to be generated by `field_id`).
128   */
129   template <uint32_t field_id>
encode_field_var_uint32(const uint32_t value)130   void encode_field_var_uint32(const uint32_t value) {
131     encode_const_var_uint<Helper::encode_field_tag(
132         field_id, Helper::WireType::WIRETYPE_VARINT)>();
133     encode_var_uint32(value);
134   }
135 
136   /**
137     Function serializes a varint32 field in case when `value` is not null.
138 
139     User need to guarantee by calling `ensure_buffer_size`
140     that the buffer has at least 15 bytes (when its not checking
141     how long field entry is going to be generated by `field_id`).
142   */
143   template <uint32_t field_id>
encode_optional_field_var_uint32(const uint32_t * value)144   void encode_optional_field_var_uint32(const uint32_t *value) {
145     if (value) encode_field_var_uint32<field_id>(*value);
146   }
147 
148   /**
149     Function serializes a varsint32 field.
150 
151     User need to guarantee by calling `ensure_buffer_size`
152     that the buffer has at least 15 bytes (when its not checking
153     how long field entry is going to be generated by `field_id`).
154   */
155   template <uint32_t field_id>
encode_field_var_sint32(const int32_t value)156   void encode_field_var_sint32(const int32_t value) {
157     encode_const_var_uint<Helper::encode_field_tag(
158         field_id, Helper::WireType::WIRETYPE_VARINT)>();
159     encode_var_sint32(value);
160   }
161 
162   /**
163     Function serializes a varsint32 field in case when `value` is not null.
164 
165     User need to guarantee by calling `ensure_buffer_size`
166     that the buffer has at least 15 bytes (when its not checking
167     how long field entry is going to be generated by `field_id`).
168   */
169   template <uint32_t field_id>
encode_optiona_field_var_sint32(const int32_t * value)170   void encode_optiona_field_var_sint32(const int32_t *value) {
171     if (value) encode_field_var_sint32<field_id>(*value);
172   }
173 
174   /**
175     Function serializes a varuint64 field.
176 
177     User need to guarantee by calling `ensure_buffer_size`
178     that the buffer has at least 20 bytes (when its not checking
179     how long field entry is going to be generated by `field_id`).
180   */
181   template <uint32_t field_id>
encode_field_var_uint64(const uint64_t value)182   void encode_field_var_uint64(const uint64_t value) {
183     encode_const_var_uint<Helper::encode_field_tag(
184         field_id, Helper::WireType::WIRETYPE_VARINT)>();
185     encode_var_uint64(value);
186   }
187 
188   /**
189     Function serializes a varuint64 field in case when `value` is not null.
190 
191     User need to guarantee by calling `ensure_buffer_size`
192     that the buffer has at least 20 bytes (when its not checking
193     how long field entry is going to be generated by `field_id`).
194   */
195   template <uint32_t field_id>
encode_optional_field_var_uint64(const uint64_t * value)196   void encode_optional_field_var_uint64(const uint64_t *value) {
197     if (value) encode_field_var_uint64<field_id>(*value);
198   }
199 
200   /**
201     Function serializes a varsint64 field.
202 
203     User need to guarantee by calling `ensure_buffer_size`
204     that the buffer has at least 20 bytes (when its not checking
205     how long field entry is going to be generated by `field_id`).
206   */
207   template <uint32_t field_id>
encode_field_var_sint64(const int64_t value)208   void encode_field_var_sint64(const int64_t value) {
209     encode_const_var_uint<Helper::encode_field_tag(
210         field_id, Helper::WireType::WIRETYPE_VARINT)>();
211     encode_var_sint64(value);
212   }
213 
214   /**
215     Function serializes a varsint64 field in case when `value` is not null.
216 
217     User need to guarantee by calling `ensure_buffer_size`
218     that the buffer has at least 20 bytes (when its not checking
219     how long field entry is going to be generated by `field_id`).
220   */
221   template <uint32_t field_id>
encode_optional_field_var_sint64(const int64_t * value)222   void encode_optional_field_var_sint64(const int64_t *value) {
223     if (value) encode_field_var_sint64<field_id>(*value);
224   }
225 
226   /**
227     Function serializes a varuint64 field using compile time informations.
228 
229     User need to guarantee by calling `ensure_buffer_size`
230     that the buffer has at least 20 bytes (when its not checking
231     how long field entry is going to be generated by `field_id`).
232   */
233   template <uint32_t field_id, uint64_t value>
encode_field_const_var_uint()234   void encode_field_const_var_uint() {
235     encode_const_var_uint<Helper::encode_field_tag(
236         field_id, Helper::WireType::WIRETYPE_VARINT)>();
237     encode_const_var_uint<value>();
238   }
239 
240   /**
241     Function serializes a varsint64 field using compile time informations.
242 
243     User need to guarantee by calling `ensure_buffer_size`
244     that the buffer has at least 20 bytes (when its not checking
245     how long field entry is going to be generated by `field_id`).
246   */
247   template <uint32_t field_id, int64_t value>
encode_field_const_var_int()248   void encode_field_const_var_int() {
249     encode_const_var_uint<Helper::encode_field_tag(
250         field_id, Helper::WireType::WIRETYPE_VARINT)>();
251     encode_const_var_sint<value>();
252   }
253 
254   /**
255     Function serializes a enum as varint32 field.
256 
257     User need to guarantee by calling `ensure_buffer_size`
258     that the buffer has at least 15 bytes (when its not checking
259     how long field entry is going to be generated by `field_id`).
260   */
261   template <uint32_t field_id>
encode_field_enum(const int32_t value)262   void encode_field_enum(const int32_t value) {
263     DBUG_ASSERT(value >= 0);
264 
265     encode_const_var_uint<Helper::encode_field_tag(
266         field_id, Helper::WireType::WIRETYPE_VARINT)>();
267     encode_var_uint32(static_cast<uint32_t>(value));
268   }
269 
270   /**
271     Function serializes a enum as varint32 field in case when `value` is not
272     null.
273 
274     User need to guarantee by calling `ensure_buffer_size`
275     that the buffer has at least 15 bytes (when its not checking
276     how long field entry is going to be generated by `field_id`).
277   */
278   template <uint32_t field_id>
encode_optional_field_enum(const int32_t * value)279   void encode_optional_field_enum(const int32_t *value) {
280     if (value) encode_field_enum<field_id>(*value);
281   }
282 
283   /**
284     Function serializes a enum as varsint64 field using compile time
285     informations.
286 
287     User need to guarantee by calling `ensure_buffer_size`
288     that the buffer has at least 20 bytes (when its not checking
289     how long field entry is going to be generated by `field_id`).
290   */
291   template <uint32_t field_id, int64_t value>
encode_field_const_enum()292   void encode_field_const_enum() {
293     static_assert(value >= 0,
294                   "This encoder doesn't support enum with negative values.");
295     encode_field_const_var_uint<field_id, value>();
296   }
297 
298   /**
299     Function serializes a field header using compile time informations.
300 
301     User need to guarantee by calling `ensure_buffer_size`
302     that the buffer has at least 10 bytes (when its not checking
303     how long field entry is going to be generated by `field_id`).
304 
305     This function requires that user will serialize manually the payload
306     of the field in subsequent call (with additional buffer size check).
307   */
308   template <uint32_t field_id>
encode_field_delimited_header()309   void encode_field_delimited_header() {
310     encode_const_var_uint<Helper::encode_field_tag(
311         field_id, Helper::WireType::WIRETYPE_LENGTH_DELIMITED)>();
312   }
313 
314   /**
315     Function serializes a raw data field.
316 
317     Thus function is going to validated the buffer size on its own,
318     user doesn't need to call `ensure_buffer_size`.
319   */
320   template <uint32_t field_id>
encode_field_delimited_raw(const uint8_t * source,uint32_t source_size)321   void encode_field_delimited_raw(const uint8_t *source, uint32_t source_size) {
322     ensure_buffer_size<10 + 5>();
323     encode_field_delimited_header<field_id>();
324     encode_var_uint32(static_cast<uint32_t>(source_size));
325 
326     encode_raw(source, source_size);
327   }
328 
329   /**
330     Function serializes a raw data field.
331 
332     Thus function is going to validated the buffer size on its own,
333     user doesn't need to call `ensure_buffer_size`.
334   */
335   template <uint32_t field_id>
encode_field_string(const std::string & value)336   void encode_field_string(const std::string &value) {
337     encode_field_delimited_raw<field_id>(
338         reinterpret_cast<const uint8_t *>(value.c_str()), value.length());
339   }
340 
341   /**
342     Function serializes a raw data field.
343 
344     Thus function is going to validated the buffer size on its own,
345     user doesn't need to call `ensure_buffer_size`.
346   */
347   template <uint32_t field_id>
encode_field_string(const char * value)348   void encode_field_string(const char *value) {
349     encode_field_delimited_raw<field_id>(
350         reinterpret_cast<const uint8_t *>(value), strlen(value));
351   }
352 
353   /**
354     Function serializes a raw data field.
355 
356     Thus function is going to validated the buffer size on its own,
357     user doesn't need to call `ensure_buffer_size`.
358   */
359   template <uint32_t field_id>
encode_field_string(const char * value,const uint32_t length)360   void encode_field_string(const char *value, const uint32_t length) {
361     encode_field_delimited_raw<field_id>(
362         reinterpret_cast<const uint8_t *>(value), length);
363   }
364 
365   /**
366     Function serializes that reserves space for integer varint with fixed size
367 
368     Thus function is going to validated the buffer size on its own,
369     user doesn't need to call `ensure_buffer_size`.
370   */
371   template <uint32_t field_id>
encode_field_fixed_uint32()372   Delayed_fixed_varuint32 encode_field_fixed_uint32() {
373     encode_const_var_uint<Helper::encode_field_tag(
374         field_id, Helper::WireType::WIRETYPE_VARINT)>();
375 
376     return Delayed_fixed_varuint32(m_page->m_current_data);
377   }
378 };
379 
380 }  // namespace protocol
381 
382 #endif  // PLUGIN_X_PROTOCOL_ENCODERS_ENCODING_PROTOBUF_H_
383