123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- //
- // Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- #ifndef BHO_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
- #define BHO_MYSQL_IMPL_INTERNAL_PROTOCOL_SERIALIZATION_HPP
- #include <asio2/bho/mysql/client_errc.hpp>
- #include <asio2/bho/mysql/error_code.hpp>
- #include <asio2/bho/mysql/field_view.hpp>
- #include <asio2/bho/mysql/string_view.hpp>
- #include <asio2/bho/mysql/impl/internal/protocol/basic_types.hpp>
- #include <asio2/bho/mysql/impl/internal/protocol/capabilities.hpp>
- #include <asio2/bho/mysql/impl/internal/protocol/protocol_field_type.hpp>
- #include <asio2/bho/assert.hpp>
- #include <asio2/bho/core/span.hpp>
- #include <asio2/bho/endian/conversion.hpp>
- #include <asio2/bho/endian/detail/endian_load.hpp>
- #include <asio2/bho/endian/detail/endian_store.hpp>
- #include <cstddef>
- #include <cstdint>
- #include <cstring>
- #include <type_traits>
- namespace bho {
- namespace mysql {
- namespace detail {
- // We operate with this enum directly in the deserialization routines for efficiency, then transform it to an
- // actual error code
- enum class deserialize_errc
- {
- ok = 0,
- incomplete_message = 1,
- protocol_value_error,
- server_unsupported
- };
- inline error_code to_error_code(deserialize_errc v) noexcept
- {
- switch (v)
- {
- case deserialize_errc::ok: return error_code();
- case deserialize_errc::incomplete_message: return error_code(client_errc::incomplete_message);
- case deserialize_errc::protocol_value_error: return error_code(client_errc::protocol_value_error);
- case deserialize_errc::server_unsupported: return error_code(client_errc::server_unsupported);
- default: BHO_ASSERT(false); return error_code(); // avoid warnings
- }
- }
- class serialization_context
- {
- std::uint8_t* first_;
- public:
- explicit serialization_context(std::uint8_t* first) noexcept : first_(first) {}
- std::uint8_t* first() const noexcept { return first_; }
- void advance(std::size_t size) noexcept { first_ += size; }
- void write(const void* buffer, std::size_t size) noexcept
- {
- if (size)
- {
- BHO_ASSERT(buffer != nullptr);
- std::memcpy(first_, buffer, size);
- advance(size);
- }
- }
- void write(std::uint8_t elm) noexcept
- {
- *first_ = elm;
- ++first_;
- }
- };
- class deserialization_context
- {
- const std::uint8_t* first_;
- const std::uint8_t* last_;
- public:
- deserialization_context(span<const std::uint8_t> data) noexcept
- : deserialization_context(data.data(), data.size())
- {
- }
- deserialization_context(const std::uint8_t* first, std::size_t size) noexcept
- : first_(first), last_(first + size){};
- const std::uint8_t* first() const noexcept { return first_; }
- const std::uint8_t* last() const noexcept { return last_; }
- void advance(std::size_t sz) noexcept
- {
- first_ += sz;
- BHO_ASSERT(last_ >= first_);
- }
- void rewind(std::size_t sz) noexcept { first_ -= sz; }
- std::size_t size() const noexcept { return last_ - first_; }
- bool empty() const noexcept { return last_ == first_; }
- bool enough_size(std::size_t required_size) const noexcept { return size() >= required_size; }
- deserialize_errc copy(void* to, std::size_t sz) noexcept
- {
- if (!enough_size(sz))
- return deserialize_errc::incomplete_message;
- memcpy(to, first_, sz);
- advance(sz);
- return deserialize_errc::ok;
- }
- string_view get_string(std::size_t sz) const noexcept
- {
- return string_view(reinterpret_cast<const char*>(first_), sz);
- }
- error_code check_extra_bytes() const noexcept
- {
- return empty() ? error_code() : error_code(client_errc::extra_bytes);
- }
- span<const std::uint8_t> to_span() const noexcept { return span<const std::uint8_t>(first_, size()); }
- };
- // integers
- template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
- deserialize_errc deserialize(deserialization_context& ctx, T& output) noexcept
- {
- constexpr std::size_t sz = sizeof(T);
- if (!ctx.enough_size(sz))
- {
- return deserialize_errc::incomplete_message;
- }
- output = endian::endian_load<T, sz, bho::endian::order::little>(ctx.first());
- ctx.advance(sz);
- return deserialize_errc::ok;
- }
- template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
- void serialize(serialization_context& ctx, T input) noexcept
- {
- endian::endian_store<T, sizeof(T), endian::order::little>(ctx.first(), input);
- ctx.advance(sizeof(T));
- }
- template <class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
- constexpr std::size_t get_size(T) noexcept
- {
- return sizeof(T);
- }
- // int3
- inline deserialize_errc deserialize(deserialization_context& ctx, int3& output) noexcept
- {
- if (!ctx.enough_size(3))
- return deserialize_errc::incomplete_message;
- output.value = endian::load_little_u24(ctx.first());
- ctx.advance(3);
- return deserialize_errc::ok;
- }
- inline void serialize(serialization_context& ctx, int3 input) noexcept
- {
- endian::store_little_u24(ctx.first(), input.value);
- ctx.advance(3);
- }
- constexpr std::size_t get_size(int3) noexcept { return 3; }
- // int_lenenc
- inline deserialize_errc deserialize(deserialization_context& ctx, int_lenenc& output) noexcept
- {
- std::uint8_t first_byte = 0;
- auto err = deserialize(ctx, first_byte);
- if (err != deserialize_errc::ok)
- {
- return err;
- }
- if (first_byte == 0xFC)
- {
- std::uint16_t value = 0;
- err = deserialize(ctx, value);
- output.value = value;
- }
- else if (first_byte == 0xFD)
- {
- int3 value{};
- err = deserialize(ctx, value);
- output.value = value.value;
- }
- else if (first_byte == 0xFE)
- {
- std::uint64_t value = 0;
- err = deserialize(ctx, value);
- output.value = value;
- }
- else
- {
- err = deserialize_errc::ok;
- output.value = first_byte;
- }
- return err;
- }
- inline void serialize(serialization_context& ctx, int_lenenc input) noexcept
- {
- if (input.value < 251)
- {
- serialize(ctx, static_cast<std::uint8_t>(input.value));
- }
- else if (input.value < 0x10000)
- {
- ctx.write(0xfc);
- serialize(ctx, static_cast<std::uint16_t>(input.value));
- }
- else if (input.value < 0x1000000)
- {
- ctx.write(0xfd);
- serialize(ctx, int3{static_cast<std::uint32_t>(input.value)});
- }
- else
- {
- ctx.write(0xfe);
- serialize(ctx, static_cast<std::uint64_t>(input.value));
- }
- }
- inline std::size_t get_size(int_lenenc input) noexcept
- {
- if (input.value < 251)
- return 1;
- else if (input.value < 0x10000)
- return 3;
- else if (input.value < 0x1000000)
- return 4;
- else
- return 9;
- }
- // protocol_field_type
- inline deserialize_errc deserialize(deserialization_context& ctx, protocol_field_type& output) noexcept
- {
- std::underlying_type<protocol_field_type>::type value = 0;
- auto err = deserialize(ctx, value);
- output = static_cast<protocol_field_type>(value);
- return err;
- }
- inline void serialize(serialization_context& ctx, protocol_field_type input) noexcept
- {
- serialize(ctx, static_cast<std::underlying_type<protocol_field_type>::type>(input));
- }
- constexpr std::size_t get_size(protocol_field_type) noexcept { return sizeof(protocol_field_type); }
- // string_fixed
- template <std::size_t N>
- deserialize_errc deserialize(deserialization_context& ctx, string_fixed<N>& output) noexcept
- {
- if (!ctx.enough_size(N))
- return deserialize_errc::incomplete_message;
- memcpy(output.value.data(), ctx.first(), N);
- ctx.advance(N);
- return deserialize_errc::ok;
- }
- template <std::size_t N>
- void serialize(serialization_context& ctx, const string_fixed<N>& input) noexcept
- {
- ctx.write(input.value.data(), N);
- }
- template <std::size_t N>
- constexpr std::size_t get_size(const string_fixed<N>&) noexcept
- {
- return N;
- }
- // string_null
- inline deserialize_errc deserialize(deserialization_context& ctx, string_null& output) noexcept
- {
- auto string_end = std::find(ctx.first(), ctx.last(), 0);
- if (string_end == ctx.last())
- {
- return deserialize_errc::incomplete_message;
- }
- std::size_t length = string_end - ctx.first();
- output.value = ctx.get_string(length);
- ctx.advance(length + 1); // skip the null terminator
- return deserialize_errc::ok;
- }
- inline void serialize(serialization_context& ctx, string_null input) noexcept
- {
- ctx.write(input.value.data(), input.value.size());
- ctx.write(0); // null terminator
- }
- inline std::size_t get_size(string_null input) noexcept { return input.value.size() + 1; }
- // string_eof
- inline deserialize_errc deserialize(deserialization_context& ctx, string_eof& output) noexcept
- {
- std::size_t size = ctx.size();
- output.value = ctx.get_string(size);
- ctx.advance(size);
- return deserialize_errc::ok;
- }
- inline void serialize(serialization_context& ctx, string_eof input) noexcept
- {
- ctx.write(input.value.data(), input.value.size());
- }
- inline std::size_t get_size(string_eof input) noexcept { return input.value.size(); }
- // string_lenenc
- inline deserialize_errc deserialize(deserialization_context& ctx, string_lenenc& output) noexcept
- {
- int_lenenc length;
- auto err = deserialize(ctx, length);
- if (err != deserialize_errc::ok)
- {
- return err;
- }
- if (length.value > (std::numeric_limits<std::size_t>::max)())
- {
- return deserialize_errc::protocol_value_error;
- }
- auto len = static_cast<std::size_t>(length.value);
- if (!ctx.enough_size(len))
- {
- return deserialize_errc::incomplete_message;
- }
- output.value = ctx.get_string(len);
- ctx.advance(len);
- return deserialize_errc::ok;
- }
- inline void serialize(serialization_context& ctx, string_lenenc input) noexcept
- {
- serialize(ctx, int_lenenc{input.value.size()});
- ctx.write(input.value.data(), input.value.size());
- }
- inline std::size_t get_size(string_lenenc input) noexcept
- {
- return get_size(int_lenenc{input.value.size()}) + input.value.size();
- }
- // serialize, deserialize, and get size of multiple fields at the same time
- template <class FirstType, class SecondType, class... Rest>
- deserialize_errc deserialize(
- deserialization_context& ctx,
- FirstType& first,
- SecondType& second,
- Rest&... tail
- ) noexcept
- {
- deserialize_errc err = deserialize(ctx, first);
- if (err == deserialize_errc::ok)
- {
- err = deserialize(ctx, second, tail...);
- }
- return err;
- }
- template <class FirstType, class SecondType, class... Rest>
- void serialize(
- serialization_context& ctx,
- const FirstType& first,
- const SecondType& second,
- const Rest&... rest
- ) noexcept
- {
- serialize(ctx, first);
- serialize(ctx, second, rest...);
- }
- template <class FirstType, class SecondType, class... Rest>
- std::size_t get_size(const FirstType& first, const SecondType& second, const Rest&... rest) noexcept
- {
- return get_size(first) + get_size(second, rest...);
- }
- // helpers
- inline string_view to_string(span<const std::uint8_t> v) noexcept
- {
- return string_view(reinterpret_cast<const char*>(v.data()), v.size());
- }
- inline span<const std::uint8_t> to_span(string_view v) noexcept
- {
- return span<const std::uint8_t>(reinterpret_cast<const std::uint8_t*>(v.data()), v.size());
- }
- } // namespace detail
- } // namespace mysql
- } // namespace bho
- #endif
|