1//
2// Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7
8#ifndef BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
9#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
10
11#include <boost/mysql/client_errc.hpp>
12#include <boost/mysql/error_code.hpp>
13#include <boost/mysql/field_view.hpp>
14#include <boost/mysql/string_view.hpp>
15
16#include <boost/mysql/impl/internal/protocol/basic_types.hpp>
17#include <boost/mysql/impl/internal/protocol/capabilities.hpp>
18#include <boost/mysql/impl/internal/protocol/protocol_field_type.hpp>
19
20#include <boost/assert.hpp>
21#include <boost/core/span.hpp>
22#include <boost/endian/conversion.hpp>
23#include <boost/endian/detail/endian_load.hpp>
24#include <boost/endian/detail/endian_store.hpp>
25
26#include <cstddef>
27#include <cstdint>
28#include <cstring>
29#include <type_traits>
30
31namespace boost {
32namespace mysql {
33namespace detail {
34
35// We operate with this enum directly in the deserialization routines for efficiency, then transform it to an
36// actual error code
37enum class deserialize_errc
38{
39 ok = 0,
40 incomplete_message = 1,
41 protocol_value_error,
42 server_unsupported
43};
44inline error_code to_error_code(deserialize_errc v) noexcept
45{
46 switch (v)
47 {
48 case deserialize_errc::ok: return error_code();
49 case deserialize_errc::incomplete_message: return error_code(client_errc::incomplete_message);
50 case deserialize_errc::protocol_value_error: return error_code(client_errc::protocol_value_error);
51 case deserialize_errc::server_unsupported: return error_code(client_errc::server_unsupported);
52 default: BOOST_ASSERT(false); return error_code(); // avoid warnings
53 }
54}
55
56class serialization_context
57{
58 std::uint8_t* first_;
59
60public:
61 explicit serialization_context(std::uint8_t* first) noexcept : first_(first) {}
62 std::uint8_t* first() const noexcept { return first_; }
63 void advance(std::size_t size) noexcept { first_ += size; }
64 void write(const void* buffer, std::size_t size) noexcept
65 {
66 if (size)
67 {
68 BOOST_ASSERT(buffer != nullptr);
69 std::memcpy(dest: first_, src: buffer, n: size);
70 advance(size);
71 }
72 }
73 void write(std::uint8_t elm) noexcept
74 {
75 *first_ = elm;
76 ++first_;
77 }
78};
79
80class deserialization_context
81{
82 const std::uint8_t* first_;
83 const std::uint8_t* last_;
84
85public:
86 deserialization_context(span<const std::uint8_t> data) noexcept
87 : deserialization_context(data.data(), data.size())
88 {
89 }
90 deserialization_context(const std::uint8_t* first, std::size_t size) noexcept
91 : first_(first), last_(first + size){};
92 const std::uint8_t* first() const noexcept { return first_; }
93 const std::uint8_t* last() const noexcept { return last_; }
94 void advance(std::size_t sz) noexcept
95 {
96 first_ += sz;
97 BOOST_ASSERT(last_ >= first_);
98 }
99 void rewind(std::size_t sz) noexcept { first_ -= sz; }
100 std::size_t size() const noexcept { return last_ - first_; }
101 bool empty() const noexcept { return last_ == first_; }
102 bool enough_size(std::size_t required_size) const noexcept { return size() >= required_size; }
103 deserialize_errc copy(void* to, std::size_t sz) noexcept
104 {
105 if (!enough_size(required_size: sz))
106 return deserialize_errc::incomplete_message;
107 memcpy(dest: to, src: first_, n: sz);
108 advance(sz);
109 return deserialize_errc::ok;
110 }
111 string_view get_string(std::size_t sz) const noexcept
112 {
113 return string_view(reinterpret_cast<const char*>(first_), sz);
114 }
115 error_code check_extra_bytes() const noexcept
116 {
117 return empty() ? error_code() : error_code(client_errc::extra_bytes);
118 }
119 span<const std::uint8_t> to_span() const noexcept { return span<const std::uint8_t>(first_, size()); }
120};
121
122// integers
123template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
124deserialize_errc deserialize(deserialization_context& ctx, T& output) noexcept
125{
126 constexpr std::size_t sz = sizeof(T);
127 if (!ctx.enough_size(required_size: sz))
128 {
129 return deserialize_errc::incomplete_message;
130 }
131 output = endian::endian_load<T, sz, boost::endian::order::little>(ctx.first());
132 ctx.advance(sz);
133 return deserialize_errc::ok;
134}
135
136template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
137void serialize(serialization_context& ctx, T input) noexcept
138{
139 endian::endian_store<T, sizeof(T), endian::order::little>(ctx.first(), input);
140 ctx.advance(size: sizeof(T));
141}
142
143template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
144constexpr std::size_t get_size(T) noexcept
145{
146 return sizeof(T);
147}
148
149// int3
150inline deserialize_errc deserialize(deserialization_context& ctx, int3& output) noexcept
151{
152 if (!ctx.enough_size(required_size: 3))
153 return deserialize_errc::incomplete_message;
154 output.value = endian::load_little_u24(p: ctx.first());
155 ctx.advance(sz: 3);
156 return deserialize_errc::ok;
157}
158inline void serialize(serialization_context& ctx, int3 input) noexcept
159{
160 endian::store_little_u24(p: ctx.first(), v: input.value);
161 ctx.advance(size: 3);
162}
163constexpr std::size_t get_size(int3) noexcept { return 3; }
164
165// int_lenenc
166inline deserialize_errc deserialize(deserialization_context& ctx, int_lenenc& output) noexcept
167{
168 std::uint8_t first_byte = 0;
169 auto err = deserialize(ctx, output&: first_byte);
170 if (err != deserialize_errc::ok)
171 {
172 return err;
173 }
174
175 if (first_byte == 0xFC)
176 {
177 std::uint16_t value = 0;
178 err = deserialize(ctx, output&: value);
179 output.value = value;
180 }
181 else if (first_byte == 0xFD)
182 {
183 int3 value{};
184 err = deserialize(ctx, output&: value);
185 output.value = value.value;
186 }
187 else if (first_byte == 0xFE)
188 {
189 std::uint64_t value = 0;
190 err = deserialize(ctx, output&: value);
191 output.value = value;
192 }
193 else
194 {
195 err = deserialize_errc::ok;
196 output.value = first_byte;
197 }
198 return err;
199}
200inline void serialize(serialization_context& ctx, int_lenenc input) noexcept
201{
202 if (input.value < 251)
203 {
204 serialize(ctx, input: static_cast<std::uint8_t>(input.value));
205 }
206 else if (input.value < 0x10000)
207 {
208 ctx.write(elm: 0xfc);
209 serialize(ctx, input: static_cast<std::uint16_t>(input.value));
210 }
211 else if (input.value < 0x1000000)
212 {
213 ctx.write(elm: 0xfd);
214 serialize(ctx, input: int3{.value: static_cast<std::uint32_t>(input.value)});
215 }
216 else
217 {
218 ctx.write(elm: 0xfe);
219 serialize(ctx, input: static_cast<std::uint64_t>(input.value));
220 }
221}
222inline std::size_t get_size(int_lenenc input) noexcept
223{
224 if (input.value < 251)
225 return 1;
226 else if (input.value < 0x10000)
227 return 3;
228 else if (input.value < 0x1000000)
229 return 4;
230 else
231 return 9;
232}
233
234// protocol_field_type
235inline deserialize_errc deserialize(deserialization_context& ctx, protocol_field_type& output) noexcept
236{
237 std::underlying_type<protocol_field_type>::type value = 0;
238 auto err = deserialize(ctx, output&: value);
239 output = static_cast<protocol_field_type>(value);
240 return err;
241}
242inline void serialize(serialization_context& ctx, protocol_field_type input) noexcept
243{
244 serialize(ctx, input: static_cast<std::underlying_type<protocol_field_type>::type>(input));
245}
246constexpr std::size_t get_size(protocol_field_type) noexcept { return sizeof(protocol_field_type); }
247
248// string_fixed
249template <std::size_t N>
250deserialize_errc deserialize(deserialization_context& ctx, string_fixed<N>& output) noexcept
251{
252 if (!ctx.enough_size(required_size: N))
253 return deserialize_errc::incomplete_message;
254 memcpy(output.value.data(), ctx.first(), N);
255 ctx.advance(sz: N);
256 return deserialize_errc::ok;
257}
258
259template <std::size_t N>
260void serialize(serialization_context& ctx, const string_fixed<N>& input) noexcept
261{
262 ctx.write(input.value.data(), N);
263}
264
265template <std::size_t N>
266constexpr std::size_t get_size(const string_fixed<N>&) noexcept
267{
268 return N;
269}
270
271// string_null
272inline deserialize_errc deserialize(deserialization_context& ctx, string_null& output) noexcept
273{
274 auto string_end = std::find(first: ctx.first(), last: ctx.last(), val: 0);
275 if (string_end == ctx.last())
276 {
277 return deserialize_errc::incomplete_message;
278 }
279 std::size_t length = string_end - ctx.first();
280 output.value = ctx.get_string(sz: length);
281 ctx.advance(sz: length + 1); // skip the null terminator
282 return deserialize_errc::ok;
283}
284inline void serialize(serialization_context& ctx, string_null input) noexcept
285{
286 ctx.write(buffer: input.value.data(), size: input.value.size());
287 ctx.write(elm: 0); // null terminator
288}
289inline std::size_t get_size(string_null input) noexcept { return input.value.size() + 1; }
290
291// string_eof
292inline deserialize_errc deserialize(deserialization_context& ctx, string_eof& output) noexcept
293{
294 std::size_t size = ctx.size();
295 output.value = ctx.get_string(sz: size);
296 ctx.advance(sz: size);
297 return deserialize_errc::ok;
298}
299inline void serialize(serialization_context& ctx, string_eof input) noexcept
300{
301 ctx.write(buffer: input.value.data(), size: input.value.size());
302}
303inline std::size_t get_size(string_eof input) noexcept { return input.value.size(); }
304
305// string_lenenc
306inline deserialize_errc deserialize(deserialization_context& ctx, string_lenenc& output) noexcept
307{
308 int_lenenc length;
309 auto err = deserialize(ctx, output&: length);
310 if (err != deserialize_errc::ok)
311 {
312 return err;
313 }
314 if (length.value > (std::numeric_limits<std::size_t>::max)())
315 {
316 return deserialize_errc::protocol_value_error;
317 }
318 auto len = static_cast<std::size_t>(length.value);
319 if (!ctx.enough_size(required_size: len))
320 {
321 return deserialize_errc::incomplete_message;
322 }
323
324 output.value = ctx.get_string(sz: len);
325 ctx.advance(sz: len);
326 return deserialize_errc::ok;
327}
328inline void serialize(serialization_context& ctx, string_lenenc input) noexcept
329{
330 serialize(ctx, input: int_lenenc{.value: input.value.size()});
331 ctx.write(buffer: input.value.data(), size: input.value.size());
332}
333inline std::size_t get_size(string_lenenc input) noexcept
334{
335 return get_size(input: int_lenenc{.value: input.value.size()}) + input.value.size();
336}
337
338// serialize, deserialize, and get size of multiple fields at the same time
339template <class FirstType, class SecondType, class... Rest>
340deserialize_errc deserialize(
341 deserialization_context& ctx,
342 FirstType& first,
343 SecondType& second,
344 Rest&... tail
345) noexcept
346{
347 deserialize_errc err = deserialize(ctx, first);
348 if (err == deserialize_errc::ok)
349 {
350 err = deserialize(ctx, second, tail...);
351 }
352 return err;
353}
354
355template <class FirstType, class SecondType, class... Rest>
356void serialize(
357 serialization_context& ctx,
358 const FirstType& first,
359 const SecondType& second,
360 const Rest&... rest
361) noexcept
362{
363 serialize(ctx, first);
364 serialize(ctx, second, rest...);
365}
366
367template <class FirstType, class SecondType, class... Rest>
368std::size_t get_size(const FirstType& first, const SecondType& second, const Rest&... rest) noexcept
369{
370 return get_size(first) + get_size(second, rest...);
371}
372
373// helpers
374inline string_view to_string(span<const std::uint8_t> v) noexcept
375{
376 return string_view(reinterpret_cast<const char*>(v.data()), v.size());
377}
378inline span<const std::uint8_t> to_span(string_view v) noexcept
379{
380 return span<const std::uint8_t>(reinterpret_cast<const std::uint8_t*>(v.data()), v.size());
381}
382
383} // namespace detail
384} // namespace mysql
385} // namespace boost
386
387#endif
388

source code of boost/libs/mysql/include/boost/mysql/impl/internal/protocol/serialization.hpp