2
0
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:
ruben
2020-03-20 15:23:28 +00:00
parent faa7d52db4
commit 2b8f2000cf
5 changed files with 108 additions and 70 deletions

View 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_ */

View File

@@ -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

View File

@@ -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; };

View File

@@ -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 {}

View File

@@ -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(