mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-20 14:52:25 +00:00
Refactored float serialization
This commit is contained in:
65
include/boost/mysql/detail/protocol/float_serialization.hpp
Normal file
65
include/boost/mysql/detail/protocol/float_serialization.hpp
Normal file
@@ -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 <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Floating points
|
||||
static_assert(std::numeric_limits<float>::is_iec559);
|
||||
static_assert(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<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 <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<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 <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::size_t>
|
||||
get_size(T, const serialization_context&) noexcept
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_FLOAT_SERIALIZATION_HPP_ */
|
||||
@@ -19,8 +19,8 @@ using binary_protocol_value = std::variant<
|
||||
int4_signed,
|
||||
int8_signed,
|
||||
string_lenenc,
|
||||
value_holder<float>,
|
||||
value_holder<double>,
|
||||
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<int8_signed, int8>(meta);
|
||||
case protocol_field_type::float_:
|
||||
return value_holder<float>();
|
||||
return float();
|
||||
case protocol_field_type::double_:
|
||||
return value_holder<double>();
|
||||
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<type, int1, int2>)
|
||||
{
|
||||
// regular promotion would make this int32_t. Force it be uint32_t
|
||||
// TODO: check here
|
||||
output = std::uint32_t(typed_protocol_value.value);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_SERIALIZATION_IPP_
|
||||
|
||||
#include <type_traits>
|
||||
#include "boost/mysql/detail/protocol/float_serialization.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
@@ -18,8 +19,6 @@ template <> struct get_serializable_type<std::uint32_t> { using type = int4; };
|
||||
template <> struct get_serializable_type<std::int32_t> { using type = int4_signed; };
|
||||
template <> struct get_serializable_type<std::uint64_t> { using type = int8; };
|
||||
template <> struct get_serializable_type<std::int64_t> { using type = int8_signed; };
|
||||
template <> struct get_serializable_type<float> { using type = value_holder<float>; };
|
||||
template <> struct get_serializable_type<double> { using type = value_holder<double>; };
|
||||
template <> struct get_serializable_type<std::string_view> { using type = string_lenenc; };
|
||||
template <> struct get_serializable_type<std::nullptr_t> { using type = dummy_serializable; };
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace detail {
|
||||
* std::size_t get_size(const T& input, const SerializationContext&) noexcept
|
||||
*/
|
||||
|
||||
// Fixed-size types
|
||||
// Fixed-size integers
|
||||
template <typename T>
|
||||
struct is_fixed_size
|
||||
{
|
||||
@@ -34,7 +34,6 @@ struct is_fixed_size
|
||||
};
|
||||
|
||||
template <> struct is_fixed_size<int_lenenc> : std::false_type {};
|
||||
template <std::size_t N> struct is_fixed_size<string_fixed<N>>: std::true_type {};
|
||||
|
||||
template <typename T> constexpr bool is_fixed_size_v = is_fixed_size<T>::value;
|
||||
|
||||
@@ -47,14 +46,6 @@ struct get_fixed_size
|
||||
|
||||
template <> struct get_fixed_size<int3> { static constexpr std::size_t value = 3; };
|
||||
template <> struct get_fixed_size<int6> { static constexpr std::size_t value = 6; };
|
||||
template <std::size_t N> struct get_fixed_size<string_fixed<N>> { static constexpr std::size_t value = N; };
|
||||
|
||||
template <typename T> void little_to_native_inplace(value_holder<T>& value) noexcept { boost::endian::little_to_native_inplace(value.value); }
|
||||
template <std::size_t size> void little_to_native_inplace(string_fixed<size>&) noexcept {}
|
||||
|
||||
template <typename T> void native_to_little_inplace(value_holder<T>& value) noexcept { boost::endian::native_to_little_inplace(value.value); }
|
||||
template <std::size_t size> void native_to_little_inplace(string_fixed<size>&) noexcept {}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<is_fixed_size<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 <typename T>
|
||||
std::enable_if_t<is_fixed_size_v<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<T>::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<const char*>(from), size);
|
||||
}
|
||||
|
||||
// string_fixed
|
||||
template <std::size_t N>
|
||||
errc deserialize(string_fixed<N>& 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 <std::size_t N>
|
||||
void serialize(const string_fixed<N>& input, serialization_context& ctx) noexcept
|
||||
{
|
||||
ctx.write(input.value.data(), N);
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr std::size_t get_size(const string_fixed<N>&, 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_holder<std::underlying_type_t<T>>>::value;
|
||||
}
|
||||
|
||||
// Floating points
|
||||
static_assert(std::numeric_limits<float>::is_iec559);
|
||||
static_assert(std::numeric_limits<double>::is_iec559);
|
||||
|
||||
template <typename T, typename=std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
errc deserialize(value_holder<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.value, buf, sizeof(T));
|
||||
#else
|
||||
std::memcpy(&output.value, ctx.first(), sizeof(T));
|
||||
#endif
|
||||
ctx.advance(sizeof(T));
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
template <typename T, typename=std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
void serialize(const value_holder<T>& 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 <typename T, typename=std::enable_if_t<std::is_floating_point_v<T>>>
|
||||
std::size_t get_size(const value_holder<T>&, 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 {}
|
||||
|
||||
@@ -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<float>(-4.2f), {0x66, 0x66, 0x86, 0xc0}, "fractional_negative"),
|
||||
SerializeParams(value_holder<float>( 4.2f), {0x66, 0x66, 0x86, 0x40}, "fractional_positive"),
|
||||
SerializeParams(value_holder<float>(3.14e20f), {0x01, 0x2d, 0x88, 0x61}, "positive_exp_positive_fractional"),
|
||||
SerializeParams(value_holder<float>(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<double>(-4.2), {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, "fractional_negative"),
|
||||
SerializeParams(value_holder<double>( 4.2), {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40}, "fractional_positive"),
|
||||
SerializeParams(value_holder<double>(3.14e200), {0xce, 0x46, 0x3c, 0x76, 0x9c, 0x68, 0x90, 0x69}, "positive_exp_positive_fractional"),
|
||||
SerializeParams(value_holder<double>(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(
|
||||
|
||||
Reference in New Issue
Block a user