From 2b8f2000cfe5179e76a4ba861dd9968d3c67ac44 Mon Sep 17 00:00:00 2001 From: ruben Date: Fri, 20 Mar 2020 15:23:28 +0000 Subject: [PATCH] Refactored float serialization --- .../detail/protocol/float_serialization.hpp | 65 ++++++++++++++ .../protocol/impl/binary_deserialization.ipp | 9 +- .../protocol/impl/binary_serialization.ipp | 3 +- .../mysql/detail/protocol/serialization.hpp | 85 +++++++------------ test/unit/detail/protocol/serialization.cpp | 16 ++-- 5 files changed, 108 insertions(+), 70 deletions(-) create mode 100644 include/boost/mysql/detail/protocol/float_serialization.hpp diff --git a/include/boost/mysql/detail/protocol/float_serialization.hpp b/include/boost/mysql/detail/protocol/float_serialization.hpp new file mode 100644 index 00000000..c6f0cc8e --- /dev/null +++ b/include/boost/mysql/detail/protocol/float_serialization.hpp @@ -0,0 +1,65 @@ +#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_FLOAT_SERIALIZATION_HPP_ +#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_FLOAT_SERIALIZATION_HPP_ + +#include "boost/mysql/detail/protocol/serialization_context.hpp" +#include "boost/mysql/detail/protocol/deserialization_context.hpp" +#include +#include + +namespace boost { +namespace mysql { +namespace detail { + +// Floating points +static_assert(std::numeric_limits::is_iec559); +static_assert(std::numeric_limits::is_iec559); + +template +std::enable_if_t, errc> +deserialize(T& output, deserialization_context& ctx) noexcept +{ + // Size check + if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message; + + // Endianness conversion + // Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI +#if BOOST_ENDIAN_BIG_BYTE + char buf [sizeof(T)]; + std::memcpy(buf, ctx.first(), sizeof(T)); + std::reverse(buf, buf + sizeof(T)); + std::memcpy(&output, buf, sizeof(T)); +#else + std::memcpy(&output, ctx.first(), sizeof(T)); +#endif + ctx.advance(sizeof(T)); + return errc::ok; +} + +template +std::enable_if_t> +serialize(T input, serialization_context& ctx) noexcept +{ + // Endianness conversion +#if BOOST_ENDIAN_BIG_BYTE + char buf [sizeof(T)]; + std::memcpy(buf, &input, sizeof(T)); + std::reverse(buf, buf + sizeof(T)); + ctx.write(buf, sizeof(T)); +#else + ctx.write(&input, sizeof(T)); +#endif +} + +template +std::enable_if_t, std::size_t> +get_size(T, const serialization_context&) noexcept +{ + return sizeof(T); +} + + +} +} +} + +#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_FLOAT_SERIALIZATION_HPP_ */ diff --git a/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp b/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp index d39b3117..fa406fec 100644 --- a/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp @@ -19,8 +19,8 @@ using binary_protocol_value = std::variant< int4_signed, int8_signed, string_lenenc, - value_holder, - value_holder, + float, + double, date, datetime, time @@ -52,9 +52,9 @@ inline binary_protocol_value get_deserializable_type( case protocol_field_type::longlong: return get_int_deserializable_type(meta); case protocol_field_type::float_: - return value_holder(); + return float(); case protocol_field_type::double_: - return value_holder(); + return double(); case protocol_field_type::timestamp: case protocol_field_type::datetime: return datetime(); @@ -105,6 +105,7 @@ inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value( else if constexpr (is_one_of_v) { // regular promotion would make this int32_t. Force it be uint32_t + // TODO: check here output = std::uint32_t(typed_protocol_value.value); } else diff --git a/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp b/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp index 7cfbdd12..cbb23ebb 100644 --- a/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp @@ -2,6 +2,7 @@ #define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_SERIALIZATION_IPP_ #include +#include "boost/mysql/detail/protocol/float_serialization.hpp" namespace boost { namespace mysql { @@ -18,8 +19,6 @@ template <> struct get_serializable_type { using type = int4; }; template <> struct get_serializable_type { using type = int4_signed; }; template <> struct get_serializable_type { using type = int8; }; template <> struct get_serializable_type { using type = int8_signed; }; -template <> struct get_serializable_type { using type = value_holder; }; -template <> struct get_serializable_type { using type = value_holder; }; template <> struct get_serializable_type { using type = string_lenenc; }; template <> struct get_serializable_type { using type = dummy_serializable; }; diff --git a/include/boost/mysql/detail/protocol/serialization.hpp b/include/boost/mysql/detail/protocol/serialization.hpp index 124f5d6b..ca1f7ccc 100644 --- a/include/boost/mysql/detail/protocol/serialization.hpp +++ b/include/boost/mysql/detail/protocol/serialization.hpp @@ -24,7 +24,7 @@ namespace detail { * std::size_t get_size(const T& input, const SerializationContext&) noexcept */ -// Fixed-size types +// Fixed-size integers template struct is_fixed_size { @@ -34,7 +34,6 @@ struct is_fixed_size }; template <> struct is_fixed_size : std::false_type {}; -template struct is_fixed_size>: std::true_type {}; template constexpr bool is_fixed_size_v = is_fixed_size::value; @@ -47,14 +46,6 @@ struct get_fixed_size template <> struct get_fixed_size { static constexpr std::size_t value = 3; }; template <> struct get_fixed_size { static constexpr std::size_t value = 6; }; -template struct get_fixed_size> { static constexpr std::size_t value = N; }; - -template void little_to_native_inplace(value_holder& value) noexcept { boost::endian::little_to_native_inplace(value.value); } -template void little_to_native_inplace(string_fixed&) noexcept {} - -template void native_to_little_inplace(value_holder& value) noexcept { boost::endian::native_to_little_inplace(value.value); } -template void native_to_little_inplace(string_fixed&) noexcept {} - template std::enable_if_t::value, errc> @@ -70,7 +61,7 @@ deserialize(T& output, deserialization_context& ctx) noexcept memset(&output.value, 0, sizeof(output.value)); memcpy(&output.value, ctx.first(), size); - little_to_native_inplace(output); + boost::endian::little_to_native_inplace(output); ctx.advance(size); return errc::ok; @@ -80,7 +71,7 @@ template std::enable_if_t> serialize(T input, serialization_context& ctx) noexcept { - native_to_little_inplace(input); + boost::endian::native_to_little_inplace(input); ctx.write(&input.value, get_fixed_size::value); } @@ -102,6 +93,31 @@ inline std::string_view get_string(const std::uint8_t* from, std::size_t size) return std::string_view (reinterpret_cast(from), size); } +// string_fixed +template +errc deserialize(string_fixed& output, deserialization_context& ctx) noexcept +{ + if (!ctx.enough_size(N)) + { + return errc::incomplete_message; + } + memcpy(&output.value, ctx.first(), N); + ctx.advance(N); + return errc::ok; +} + +template +void serialize(const string_fixed& input, serialization_context& ctx) noexcept +{ + ctx.write(input.value.data(), N); +} + +template +constexpr std::size_t get_size(const string_fixed&, const serialization_context&) noexcept +{ + return N; +} + // string_null inline errc deserialize(string_null& output, deserialization_context& ctx) noexcept; inline void serialize(string_null input, serialization_context& ctx) noexcept @@ -164,49 +180,6 @@ std::size_t get_size(T, const serialization_context&) noexcept return get_fixed_size>>::value; } -// Floating points -static_assert(std::numeric_limits::is_iec559); -static_assert(std::numeric_limits::is_iec559); - -template >> -errc deserialize(value_holder& output, deserialization_context& ctx) noexcept -{ - // Size check - if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message; - - // Endianness conversion - // Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI -#if BOOST_ENDIAN_BIG_BYTE - char buf [sizeof(T)]; - std::memcpy(buf, ctx.first(), sizeof(T)); - std::reverse(buf, buf + sizeof(T)); - std::memcpy(&output.value, buf, sizeof(T)); -#else - std::memcpy(&output.value, ctx.first(), sizeof(T)); -#endif - ctx.advance(sizeof(T)); - return errc::ok; -} - -template >> -void serialize(const value_holder& input, serialization_context& ctx) noexcept -{ - // Endianness conversion -#if BOOST_ENDIAN_BIG_BYTE - char buf [sizeof(T)]; - std::memcpy(buf, &input.value, sizeof(T)); - std::reverse(buf, buf + sizeof(T)); - ctx.write(buf, sizeof(T)); -#else - ctx.write(&input.value, sizeof(T)); -#endif -} - -template >> -std::size_t get_size(const value_holder&, const serialization_context&) noexcept -{ - return sizeof(T); -} // Structs. To allow a limited way of reflection, structs should // specialize get_struct_fields with a tuple of pointers to members, @@ -363,7 +336,7 @@ void serialize_fields(serialization_context& ctx, const FirstType& field, const // Dummy type to indicate no (de)serialization is required struct dummy_serializable { - dummy_serializable(...) {} // Make it constructible from anything + explicit dummy_serializable(...) {} // Make it constructible from anything }; inline std::size_t get_size(dummy_serializable, const serialization_context&) noexcept { return 0; } inline void serialize(dummy_serializable, serialization_context&) noexcept {} diff --git a/test/unit/detail/protocol/serialization.cpp b/test/unit/detail/protocol/serialization.cpp index 9062289f..b0669a51 100644 --- a/test/unit/detail/protocol/serialization.cpp +++ b/test/unit/detail/protocol/serialization.cpp @@ -125,17 +125,17 @@ INSTANTIATE_TEST_SUITE_P(Enums, FullSerializationTest, ::testing::Values( // Other binary values INSTANTIATE_TEST_SUITE_P(Float, FullSerializationTest, ::testing::Values( - SerializeParams(value_holder(-4.2f), {0x66, 0x66, 0x86, 0xc0}, "fractional_negative"), - SerializeParams(value_holder( 4.2f), {0x66, 0x66, 0x86, 0x40}, "fractional_positive"), - SerializeParams(value_holder(3.14e20f), {0x01, 0x2d, 0x88, 0x61}, "positive_exp_positive_fractional"), - SerializeParams(value_holder(0.0f), {0x00, 0x00, 0x00, 0x00}, "zero") + SerializeParams(-4.2f, {0x66, 0x66, 0x86, 0xc0}, "fractional_negative"), + SerializeParams(4.2f, {0x66, 0x66, 0x86, 0x40}, "fractional_positive"), + SerializeParams(3.14e20f, {0x01, 0x2d, 0x88, 0x61}, "positive_exp_positive_fractional"), + SerializeParams(0.0f, {0x00, 0x00, 0x00, 0x00}, "zero") ), test_name_generator); INSTANTIATE_TEST_SUITE_P(Double, FullSerializationTest, ::testing::Values( - SerializeParams(value_holder(-4.2), {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, "fractional_negative"), - SerializeParams(value_holder( 4.2), {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40}, "fractional_positive"), - SerializeParams(value_holder(3.14e200), {0xce, 0x46, 0x3c, 0x76, 0x9c, 0x68, 0x90, 0x69}, "positive_exp_positive_fractional"), - SerializeParams(value_holder(0.0), {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "zero") + SerializeParams(-4.2, {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, "fractional_negative"), + SerializeParams(4.2, {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40}, "fractional_positive"), + SerializeParams(3.14e200, {0xce, 0x46, 0x3c, 0x76, 0x9c, 0x68, 0x90, 0x69}, "positive_exp_positive_fractional"), + SerializeParams(0.0, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "zero") ), test_name_generator); INSTANTIATE_TEST_SUITE_P(Date, FullSerializationTest, ::testing::Values(