2
0
mirror of https://github.com/boostorg/mysql.git synced 2026-02-15 01:02:17 +00:00

Converted serialization to a trait-based system

This commit is contained in:
ruben
2020-03-21 17:12:53 +00:00
parent d4e7d4627a
commit db1e3005bb
13 changed files with 684 additions and 390 deletions

View File

@@ -1,8 +1,13 @@
Name change
Get rid of get_struct_fields
Put serialization specializations together with the messages
Split serialization tests according to file structure
Split impls to impl header
Get rid of contexts files
Transform tags into an enum
serialization_test_common
Should be in unit/
Should not be header only
Should not require including all messages
Should have things in namespace test, not detail
Split serialization tests into chunks following file structure
Try to make string_fixed just an array of chars instead of a value_holder

View File

@@ -1,11 +1,9 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_COMMON_MESSAGES_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_COMMON_MESSAGES_HPP_
#include "boost/mysql/detail/protocol/deserialization_context.hpp"
#include "boost/mysql/detail/auxiliar/get_struct_fields.hpp"
#include "boost/mysql/detail/protocol/serialization.hpp"
#include "boost/mysql/detail/protocol/constants.hpp"
#include "boost/mysql/collation.hpp"
#include "boost/mysql/error.hpp"
#include <tuple>
namespace boost {
@@ -103,8 +101,18 @@ struct get_struct_fields<column_definition_packet>
);
};
inline errc deserialize(ok_packet& output, deserialization_context& ctx) noexcept;
inline errc deserialize(column_definition_packet& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<ok_packet, struct_tag> : noop_serialization_traits
{
static inline errc deserialize_(ok_packet& output, deserialization_context& ctx) noexcept;
};
template <>
struct serialization_traits<column_definition_packet, struct_tag> : noop_serialization_traits
{
static inline errc deserialize_(column_definition_packet& output, deserialization_context& ctx) noexcept;
};
inline error_code process_error_packet(deserialization_context& ctx, error_info& info);
} // detail

View File

@@ -1,11 +1,7 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_HANDSHAKE_MESSAGES_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_HANDSHAKE_MESSAGES_HPP_
#include "boost/mysql/detail/protocol/serialization_context.hpp"
#include "boost/mysql/detail/protocol/deserialization_context.hpp"
#include "boost/mysql/detail/protocol/protocol_types.hpp"
#include "boost/mysql/detail/auxiliar/get_struct_fields.hpp"
#include "boost/mysql/error.hpp"
#include "boost/mysql/detail/protocol/serialization.hpp"
namespace boost {
namespace mysql {
@@ -94,10 +90,24 @@ struct get_struct_fields<auth_switch_response_packet>
);
};
inline errc deserialize(handshake_packet& output, deserialization_context& ctx) noexcept;
inline std::size_t get_size(const handshake_response_packet& value, const serialization_context& ctx) noexcept;
inline void serialize(const handshake_response_packet& value, serialization_context& ctx) noexcept;
inline errc deserialize(auth_switch_request_packet& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<handshake_packet, struct_tag> : noop_serialization_traits
{
static inline errc deserialize_(handshake_packet& output, deserialization_context& ctx) noexcept;
};
template <>
struct serialization_traits<handshake_response_packet, struct_tag> : noop_serialization_traits
{
static inline std::size_t get_size_(const handshake_response_packet& value, const serialization_context& ctx) noexcept;
static inline void serialize_(const handshake_response_packet& value, serialization_context& ctx) noexcept;
};
template <>
struct serialization_traits<auth_switch_request_packet, struct_tag> : noop_serialization_traits
{
static inline errc deserialize_(auth_switch_request_packet& output, deserialization_context& ctx) noexcept;
};
} // detail

View File

@@ -1,9 +1,11 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_COMMON_MESSAGES_IPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_COMMON_MESSAGES_IPP_
#include "boost/mysql/detail/protocol/serialization.hpp"
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::ok_packet,
boost::mysql::detail::struct_tag
>::deserialize_(
ok_packet& output,
deserialization_context& ctx
) noexcept
@@ -24,7 +26,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
}
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::column_definition_packet,
boost::mysql::detail::struct_tag
>::deserialize_(
column_definition_packet& output,
deserialization_context& ctx
) noexcept

View File

@@ -1,9 +1,12 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_HANDSHAKE_MESSAGES_IPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_HANDSHAKE_MESSAGES_IPP_
#include "boost/mysql/detail/protocol/serialization.hpp"
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_packet,
boost::mysql::detail::struct_tag
>::deserialize_(
handshake_packet& output,
deserialization_context& ctx
) noexcept
@@ -67,7 +70,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return errc::ok;
}
std::size_t boost::mysql::detail::get_size(
std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::struct_tag
>::get_size_(
const handshake_response_packet& value,
const serialization_context& ctx
) noexcept
@@ -87,7 +94,11 @@ std::size_t boost::mysql::detail::get_size(
return res;
}
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::struct_tag
>::serialize_(
const handshake_response_packet& value,
serialization_context& ctx
) noexcept
@@ -106,7 +117,11 @@ inline void boost::mysql::detail::serialize(
serialize(value.client_plugin_name, ctx);
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::auth_switch_request_packet,
boost::mysql::detail::struct_tag
>::deserialize_(
auth_switch_request_packet& output,
deserialization_context& ctx
) noexcept

View File

@@ -1,7 +1,6 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_PREPARED_STATEMENT_MESSAGES_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_PREPARED_STATEMENT_MESSAGES_HPP_
#include "boost/mysql/detail/protocol/serialization.hpp"
#include "boost/mysql/detail/protocol/null_bitmap_traits.hpp"
namespace boost {
@@ -88,7 +87,11 @@ inline void serialize_binary_value(
} // mysql
} // boost
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_prepare_ok_packet,
boost::mysql::detail::struct_tag
>::deserialize_(
com_stmt_prepare_ok_packet& output,
deserialization_context& ctx
) noexcept
@@ -105,7 +108,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
}
template <typename ForwardIterator>
inline std::size_t boost::mysql::detail::get_size(
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::struct_tag
>::get_size_(
const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx
) noexcept
@@ -127,7 +134,11 @@ inline std::size_t boost::mysql::detail::get_size(
}
template <typename ForwardIterator>
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::struct_tag
>::serialize_(
const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx
) noexcept

View File

@@ -0,0 +1,310 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_HPP_
namespace boost {
namespace mysql {
namespace detail {
template <typename T>
constexpr std::size_t get_int_size()
{
static_assert(is_fixed_size_int<T>());
return std::is_same<T, int3>::value ? 3 :
std::is_same<T, int6>::value ? 6 : sizeof(T);
}
struct is_command_helper
{
template <typename T>
static constexpr std::true_type get(decltype(T::command_id)*);
template <typename T>
static constexpr std::false_type get(...);
};
template <typename T>
struct is_command : decltype(is_command_helper::get<T>(nullptr))
{
};
template <std::size_t index, typename T>
errc deserialize_struct(
[[maybe_unused]] T& output,
[[maybe_unused]] deserialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return errc::ok;
}
else
{
constexpr auto pmem = std::get<index>(fields);
errc err = deserialize(output.*pmem, ctx);
if (err != errc::ok)
{
return err;
}
else
{
return deserialize_struct<index+1>(output, ctx);
}
}
}
template <std::size_t index, typename T>
void serialize_struct(
[[maybe_unused]] const T& value,
[[maybe_unused]] serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index < std::tuple_size<decltype(fields)>::value)
{
auto pmem = std::get<index>(fields);
serialize(value.*pmem, ctx);
serialize_struct<index+1>(value, ctx);
}
}
template <std::size_t index, typename T>
std::size_t get_size_struct(
[[maybe_unused]] const T& input,
[[maybe_unused]] const serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return 0;
}
else
{
constexpr auto pmem = std::get<index>(fields);
return get_size_struct<index+1>(input, ctx) +
get_size(input.*pmem, ctx);
}
}
} // detail
} // mysql
} // boost
template <typename T>
constexpr bool boost::mysql::detail::is_fixed_size_int()
{
return
std::is_integral<get_value_type_t<T>>::value &&
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value &&
!std::is_same<T, int_lenenc>::value;
}
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::fixed_size_int_tag
>::deserialize_(
T& output,
deserialization_context& ctx
) noexcept
{
static_assert(std::is_standard_layout_v<decltype(T::value)>);
constexpr auto size = get_int_size<T>();
if (!ctx.enough_size(size))
{
return errc::incomplete_message;
}
memset(&output.value, 0, sizeof(output.value));
memcpy(&output.value, ctx.first(), size);
boost::endian::little_to_native_inplace(output);
ctx.advance(size);
return errc::ok;
}
template <typename T>
void boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::fixed_size_int_tag
>::serialize_(
T input,
serialization_context& ctx
) noexcept
{
boost::endian::native_to_little_inplace(input);
ctx.write(&input.value, get_int_size<T>());
}
template <typename T>
constexpr std::size_t boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::fixed_size_int_tag
>::get_size_(T, const serialization_context&) noexcept
{
return get_int_size<T>();
}
template <std::size_t N>
boost::mysql::errc boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_fixed<N>,
boost::mysql::detail::no_serialization_tag
>::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 <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::enum_tag
>::deserialize_(
T& output,
deserialization_context& ctx
) noexcept
{
serializable_type value;
errc err = deserialize(value, ctx);
if (err != errc::ok)
{
return err;
}
output = static_cast<T>(value.value);
return errc::ok;
}
template <typename T>
std::size_t boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::enum_tag
>::get_size_(T, const serialization_context&) noexcept
{
return get_int_size<serializable_type>();
}
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::floating_point_tag
>::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>
void
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::floating_point_tag
>::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>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::struct_tag
>::deserialize_(T& output, deserialization_context& ctx) noexcept
{
return deserialize_struct<0>(output, ctx);
}
template <typename T>
void
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::struct_tag
>::serialize_(
[[maybe_unused]] const T& input,
[[maybe_unused]] serialization_context& ctx
) noexcept
{
// For commands, add the command ID. Commands are only sent by the client,
// so this is not considered in the deserialization functions.
if constexpr (is_command<T>::value)
{
serialize(int1(T::command_id), ctx);
}
serialize_struct<0>(input, ctx);
}
template <typename T>
std::size_t
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::struct_tag
>::get_size_(const T& input, const serialization_context& ctx) noexcept
{
std::size_t res = is_command<T>::value ? 1 : 0;
res += get_size_struct<0>(input, ctx);
return res;
}
template <typename Serializable, typename Allocator>
void boost::mysql::detail::serialize_message(
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
)
{
serialization_context ctx (caps);
std::size_t size = get_size(input, ctx);
buffer.resize(size);
ctx.set_first(buffer.data());
serialize(input, ctx);
assert(ctx.first() == buffer.data() + buffer.size());
}
template <typename Deserializable>
boost::mysql::error_code boost::mysql::detail::deserialize_message(
Deserializable& output,
deserialization_context& ctx
)
{
auto err = deserialize(output, ctx);
if (err != errc::ok) return make_error_code(err);
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
}
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_HPP_ */

View File

@@ -5,6 +5,14 @@ namespace boost {
namespace mysql {
namespace detail {
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);
}
inline errc deserialize_binary_date(
date& output,
std::uint8_t length,
@@ -110,7 +118,11 @@ struct broken_time
} // mysql
} // boost
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
int_lenenc& output,
deserialization_context& ctx
) noexcept
@@ -148,7 +160,12 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return err;
}
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::no_serialization_tag
>::serialize_(
int_lenenc input,
serialization_context& ctx
) noexcept
@@ -174,9 +191,13 @@ inline void boost::mysql::detail::serialize(
}
}
inline std::size_t boost::mysql::detail::get_size(
int_lenenc input, const
serialization_context&
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::no_serialization_tag
>::get_size_(
int_lenenc input,
const serialization_context&
) noexcept
{
if (input.value < 251) return 1;
@@ -185,7 +206,11 @@ inline std::size_t boost::mysql::detail::get_size(
else return 9;
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_null,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
string_null& output,
deserialization_context& ctx
) noexcept
@@ -200,7 +225,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return errc::ok;
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_eof,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
string_eof& output,
deserialization_context& ctx
) noexcept
@@ -210,7 +239,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return errc::ok;
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_lenenc,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
string_lenenc& output,
deserialization_context& ctx
) noexcept
@@ -231,7 +264,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return errc::ok;
}
inline std::size_t boost::mysql::detail::get_size(
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::no_serialization_tag
>::get_size_(
const date&,
const serialization_context&
) noexcept
@@ -240,7 +277,11 @@ inline std::size_t boost::mysql::detail::get_size(
return 5; // length, year, month, day
}
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::no_serialization_tag
>::serialize_(
const date& input,
serialization_context& ctx
) noexcept
@@ -250,7 +291,11 @@ inline void boost::mysql::detail::serialize(
serialize_binary_ymd(::date::year_month_day (input), ctx);
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
date& output,
deserialization_context& ctx
) noexcept
@@ -262,7 +307,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
}
// datetime
inline std::size_t boost::mysql::detail::get_size(
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::no_serialization_tag
>::get_size_(
const datetime& input,
const serialization_context&
) noexcept
@@ -271,7 +320,11 @@ inline std::size_t boost::mysql::detail::get_size(
return dt.binary_serialized_length() + 1; // extra length prefix byte
}
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::no_serialization_tag
>::serialize_(
const datetime& input,
serialization_context& ctx
) noexcept
@@ -299,7 +352,11 @@ inline void boost::mysql::detail::serialize(
}
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
datetime& output,
deserialization_context& ctx
) noexcept
@@ -337,7 +394,11 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
}
// time
inline std::size_t boost::mysql::detail::get_size(
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::no_serialization_tag
>::get_size_(
const time& input,
const serialization_context&
) noexcept
@@ -345,7 +406,11 @@ inline std::size_t boost::mysql::detail::get_size(
return broken_time(input).binary_serialized_length() + 1; // length byte
}
inline void boost::mysql::detail::serialize(
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::no_serialization_tag
>::serialize_(
const time& input,
serialization_context& ctx
) noexcept
@@ -372,7 +437,11 @@ inline void boost::mysql::detail::serialize(
}
}
inline boost::mysql::errc boost::mysql::detail::deserialize(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::no_serialization_tag
>::deserialize_(
time& output,
deserialization_context& ctx
) noexcept
@@ -417,33 +486,6 @@ inline boost::mysql::errc boost::mysql::detail::deserialize(
return errc::ok;
}
template <typename Serializable, typename Allocator>
void boost::mysql::detail::serialize_message(
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
)
{
serialization_context ctx (caps);
std::size_t size = get_size(input, ctx);
buffer.resize(size);
ctx.set_first(buffer.data());
serialize(input, ctx);
assert(ctx.first() == buffer.data() + buffer.size());
}
template <typename Deserializable>
boost::mysql::error_code boost::mysql::detail::deserialize_message(
Deserializable& output,
deserialization_context& ctx
)
{
auto err = deserialize(output, ctx);
if (err != errc::ok) return make_error_code(err);
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
}
inline std::pair<boost::mysql::error_code, std::uint8_t>
boost::mysql::detail::deserialize_message_type(
deserialization_context& ctx
@@ -464,6 +506,4 @@ boost::mysql::detail::deserialize_message_type(
}
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_IPP_ */

View File

@@ -1,13 +1,9 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_PREPARED_STATEMENT_MESSAGES_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_PREPARED_STATEMENT_MESSAGES_HPP_
#include "boost/mysql/detail/protocol/protocol_types.hpp"
#include "boost/mysql/detail/protocol/serialization_context.hpp"
#include "boost/mysql/detail/protocol/deserialization_context.hpp"
#include "boost/mysql/detail/protocol/serialization.hpp"
#include "boost/mysql/detail/protocol/constants.hpp"
#include "boost/mysql/detail/auxiliar/get_struct_fields.hpp"
#include "boost/mysql/value.hpp"
#include "boost/mysql/error.hpp"
namespace boost {
namespace mysql {
@@ -107,13 +103,20 @@ struct get_struct_fields<com_stmt_close_packet>
);
};
inline errc deserialize(com_stmt_prepare_ok_packet& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<com_stmt_prepare_ok_packet, struct_tag> : noop_serialization_traits
{
static inline errc deserialize_(com_stmt_prepare_ok_packet& output, deserialization_context& ctx) noexcept;
};
template <typename FowardIterator>
inline std::size_t get_size(const com_stmt_execute_packet<FowardIterator>& value, const serialization_context& ctx) noexcept;
template <typename FowardIterator>
inline void serialize(const com_stmt_execute_packet<FowardIterator>& input, serialization_context& ctx) noexcept;
template <typename ForwardIterator>
struct serialization_traits<com_stmt_execute_packet<ForwardIterator>, struct_tag>: noop_serialization_traits
{
static inline std::size_t get_size_(const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx) noexcept;
static inline void serialize_(const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx) noexcept;
};
} // detail
} // mysql

View File

@@ -1,8 +1,7 @@
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_QUERY_MESSAGES_HPP_
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_QUERY_MESSAGES_HPP_
#include "boost/mysql/detail/protocol/protocol_types.hpp"
#include "boost/mysql/detail/auxiliar/get_struct_fields.hpp"
#include "boost/mysql/detail/protocol/serialization.hpp"
#include <tuple>
namespace boost {

View File

@@ -16,346 +16,200 @@ namespace boost {
namespace mysql {
namespace detail {
struct no_serialization_tag {};
/**
* Base forms:
* errc deserialize(T& output, deserialization_context&) noexcept
* void serialize(const T& input, SerializationContext&) noexcept
* std::size_t get_size(const T& input, const SerializationContext&) noexcept
*/
template <typename T>
struct get_serialization_tag;
template <typename T, typename Tag=typename get_serialization_tag<T>::type>
struct serialization_traits;
template <typename T>
errc deserialize(T& output, deserialization_context& ctx) noexcept
{
return serialization_traits<T>::deserialize_(output, ctx);
}
template <typename T>
void serialize(const T& input, serialization_context& ctx) noexcept
{
serialization_traits<T>::serialize_(input, ctx);
}
template <typename T>
std::size_t get_size(const T& input, const serialization_context& ctx) noexcept
{
return serialization_traits<T>::get_size_(input, ctx);
}
// Fixed-size integers
struct fixed_size_int_tag {};
template <typename T>
struct is_fixed_size_int
constexpr bool is_fixed_size_int();
template <typename T>
struct serialization_traits<T, fixed_size_int_tag>
{
static constexpr bool value =
std::is_integral<get_value_type_t<T>>::value &&
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static constexpr std::size_t get_size_(T, const serialization_context&) noexcept;
};
template <> struct is_fixed_size_int<int_lenenc> : std::false_type {};
template <typename T> constexpr bool is_fixed_size_v = is_fixed_size_int<T>::value;
template <typename T>
struct get_int_size
{
static_assert(is_fixed_size_v<T>);
static constexpr std::size_t value = sizeof(T::value);
};
template <> struct get_int_size<int3> { static constexpr std::size_t value = 3; };
template <> struct get_int_size<int6> { static constexpr std::size_t value = 6; };
template <typename T>
std::enable_if_t<is_fixed_size_int<T>::value, errc>
deserialize(T& output, deserialization_context& ctx) noexcept
{
static_assert(std::is_standard_layout_v<decltype(T::value)>);
constexpr auto size = get_int_size<T>::value;
if (!ctx.enough_size(size))
{
return errc::incomplete_message;
}
memset(&output.value, 0, sizeof(output.value));
memcpy(&output.value, ctx.first(), size);
boost::endian::little_to_native_inplace(output);
ctx.advance(size);
return errc::ok;
}
template <typename T>
std::enable_if_t<is_fixed_size_v<T>>
serialize(T input, serialization_context& ctx) noexcept
{
boost::endian::native_to_little_inplace(input);
ctx.write(&input.value, get_int_size<T>::value);
}
template <typename T>
constexpr std::enable_if_t<is_fixed_size_v<T>, std::size_t>
get_size(T, const serialization_context&) noexcept
{
return get_int_size<T>::value;
}
// int_lenenc
inline errc deserialize(int_lenenc& output, deserialization_context& ctx) noexcept;
inline void serialize(int_lenenc input, serialization_context& ctx) noexcept;
inline std::size_t get_size(int_lenenc input, const serialization_context&) noexcept;
// Helper for strings
inline std::string_view get_string(const std::uint8_t* from, std::size_t size)
template <>
struct serialization_traits<int_lenenc, no_serialization_tag>
{
return std::string_view (reinterpret_cast<const char*>(from), size);
}
static inline errc deserialize_(int_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(int_lenenc input, serialization_context& ctx) noexcept;
static inline std::size_t get_size_(int_lenenc input, const serialization_context&) noexcept;
};
// string_fixed
template <std::size_t N>
errc deserialize(string_fixed<N>& output, deserialization_context& ctx) noexcept
struct serialization_traits<string_fixed<N>, no_serialization_tag>
{
if (!ctx.enough_size(N))
static errc deserialize_(string_fixed<N>& output, deserialization_context& ctx) noexcept;
static void serialize_(const string_fixed<N>& input, serialization_context& ctx) noexcept
{
return errc::incomplete_message;
ctx.write(input.value.data(), N);
}
memcpy(&output.value, ctx.first(), N);
ctx.advance(N);
return errc::ok;
}
static constexpr std::size_t get_size_(const string_fixed<N>&, const serialization_context&) noexcept
{
return N;
}
};
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
template <>
struct serialization_traits<string_null, no_serialization_tag>
{
ctx.write(input.value.data(), input.value.size());
ctx.write(0); // null terminator
}
inline std::size_t get_size(string_null input, const serialization_context&) noexcept
{
return input.value.size() + 1;
}
static inline errc deserialize_(string_null& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_null input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
ctx.write(0); // null terminator
}
static inline std::size_t get_size_(string_null input, const serialization_context&) noexcept
{
return input.value.size() + 1;
}
};
// string_eof
inline errc deserialize(string_eof& output, deserialization_context& ctx) noexcept;
inline void serialize(string_eof input, serialization_context& ctx) noexcept
template <>
struct serialization_traits<string_eof, no_serialization_tag>
{
ctx.write(input.value.data(), input.value.size());
}
inline std::size_t get_size(string_eof input, const serialization_context&) noexcept
{
return input.value.size();
}
static inline errc deserialize_(string_eof& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_eof input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_eof input, const serialization_context&) noexcept
{
return input.value.size();
}
};
// string_lenenc
inline errc deserialize(string_lenenc& output, deserialization_context& ctx) noexcept;
inline void serialize(string_lenenc input, serialization_context& ctx) noexcept
template <>
struct serialization_traits<string_lenenc, no_serialization_tag>
{
serialize(int_lenenc(input.value.size()), ctx);
ctx.write(input.value.data(), input.value.size());
}
inline std::size_t get_size(string_lenenc input, const serialization_context& ctx) noexcept
{
return get_size(int_lenenc(input.value.size()), ctx) + input.value.size();
}
static inline errc deserialize_(string_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_lenenc input, serialization_context& ctx) noexcept
{
serialize(int_lenenc(input.value.size()), ctx);
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_lenenc input, const serialization_context& ctx) noexcept
{
return get_size(int_lenenc(input.value.size()), ctx) + input.value.size();
}
};
// Enums
template <typename T, typename=std::enable_if_t<std::is_enum_v<T>>>
errc deserialize(T& output, deserialization_context& ctx) noexcept
struct enum_tag {};
template <typename T>
struct serialization_traits<T, enum_tag>
{
value_holder<std::underlying_type_t<T>> value;
errc err = deserialize(value, ctx);
if (err != errc::ok)
using underlying_type = std::underlying_type_t<T>;
using serializable_type = value_holder<underlying_type>;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept
{
return err;
serialize(serializable_type(static_cast<underlying_type>(input)), ctx);
}
output = static_cast<T>(value.value);
return errc::ok;
}
template <typename T, typename=std::enable_if_t<std::is_enum_v<T>>>
void serialize(T input, serialization_context& ctx) noexcept
{
value_holder<std::underlying_type_t<T>> value {static_cast<std::underlying_type_t<T>>(input)};
serialize(value, ctx);
}
template <typename T, typename=std::enable_if_t<std::is_enum_v<T>>>
std::size_t get_size(T, const serialization_context&) noexcept
{
return get_int_size<value_holder<std::underlying_type_t<T>>>::value;
}
static std::size_t get_size_(T, const serialization_context&) noexcept;
};
// Floating points
static_assert(std::numeric_limits<float>::is_iec559);
static_assert(std::numeric_limits<double>::is_iec559);
struct floating_point_tag {};
template <typename T>
std::enable_if_t<std::is_floating_point_v<T>, errc>
deserialize(T& output, deserialization_context& ctx) noexcept
struct serialization_traits<T, floating_point_tag>
{
// Size check
if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message;
static_assert(std::numeric_limits<T>::is_iec559);
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static std::size_t get_size_(T, const serialization_context&) noexcept
{
return sizeof(T);
}
};
// 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);
}
// Dates and times
inline std::size_t get_size(const date& input, const serialization_context& ctx) noexcept;
inline void serialize(const date& input, serialization_context& ctx) noexcept;
inline errc deserialize(date& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<date, no_serialization_tag>
{
static inline std::size_t get_size_(const date& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const date& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(date& output, deserialization_context& ctx) noexcept;
};
inline std::size_t get_size(const datetime& input, const serialization_context& ctx) noexcept;
inline void serialize(const datetime& input, serialization_context& ctx) noexcept;
inline errc deserialize(datetime& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<datetime, no_serialization_tag>
{
static inline std::size_t get_size_(const datetime& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const datetime& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(datetime& output, deserialization_context& ctx) noexcept;
};
inline std::size_t get_size(const time& input, const serialization_context& ctx) noexcept;
inline void serialize(const time& input, serialization_context& ctx) noexcept;
inline errc deserialize(time& output, deserialization_context& ctx) noexcept;
template <>
struct serialization_traits<time, no_serialization_tag>
{
static inline std::size_t get_size_(const time& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const time& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(time& output, deserialization_context& ctx) noexcept;
};
// Structs and commands (messages)
struct struct_tag {};
template <typename T>
struct is_struct_with_fields
constexpr bool is_struct_with_fields()
{
static constexpr bool value = !std::is_same_v<
return !std::is_same_v<
std::decay_t<decltype(get_struct_fields<T>::value)>,
not_a_struct_with_fields
>;
}
template <typename T>
struct serialization_traits<T, struct_tag>
{
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(const T& input, serialization_context& ctx) noexcept;
static std::size_t get_size_(const T& input, const serialization_context& ctx) noexcept;
};
struct is_command_helper
{
template <typename T>
static constexpr std::true_type get(decltype(T::command_id)*);
template <typename T>
static constexpr std::false_type get(...);
};
template <typename T>
struct is_command : decltype(is_command_helper::get<T>(nullptr))
{
};
template <std::size_t index, typename T>
errc deserialize_struct(
[[maybe_unused]] T& output,
[[maybe_unused]] deserialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return errc::ok;
}
else
{
constexpr auto pmem = std::get<index>(fields);
errc err = deserialize(output.*pmem, ctx);
if (err != errc::ok)
{
return err;
}
else
{
return deserialize_struct<index+1>(output, ctx);
}
}
}
template <typename T>
std::enable_if_t<is_struct_with_fields<T>::value, errc>
deserialize(T& output, deserialization_context& ctx) noexcept
{
return deserialize_struct<0>(output, ctx);
}
template <std::size_t index, typename T>
void serialize_struct(
[[maybe_unused]] const T& value,
[[maybe_unused]] serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index < std::tuple_size<decltype(fields)>::value)
{
auto pmem = std::get<index>(fields);
serialize(value.*pmem, ctx);
serialize_struct<index+1>(value, ctx);
}
}
template <typename T>
std::enable_if_t<is_struct_with_fields<T>::value>
serialize(
[[maybe_unused]] const T& input,
[[maybe_unused]] serialization_context& ctx
) noexcept
{
// For commands, add the command ID. Commands are only sent by the client,
// so this is not considered in the deserialization functions.
if constexpr (is_command<T>::value)
{
serialize(int1(T::command_id), ctx);
}
serialize_struct<0>(input, ctx);
}
template <std::size_t index, typename T>
std::size_t get_size_struct(
[[maybe_unused]] const T& input,
[[maybe_unused]] const serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return 0;
}
else
{
constexpr auto pmem = std::get<index>(fields);
return get_size_struct<index+1>(input, ctx) +
get_size(input.*pmem, ctx);
}
}
template <typename T>
std::enable_if_t<is_struct_with_fields<T>::value, std::size_t>
get_size(const T& input, const serialization_context& ctx) noexcept
{
std::size_t res = is_command<T>::value ? 1 : 0;
res += get_size_struct<0>(input, ctx);
return res;
}
// Helper to serialize top-level messages
template <typename Serializable, typename Allocator>
void serialize_message(
@@ -370,17 +224,30 @@ error_code deserialize_message(
deserialization_context& ctx
);
// Dummy type to indicate no (de)serialization is required
struct dummy_serializable
{
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 {}
inline errc deserialize(dummy_serializable, deserialization_context&) noexcept { return errc::ok; }
/*struct noop_serialization_traits
{
static inline std::size_t get_size_(dummy_serializable, const serialization_context&) noexcept { return 0; }
static inline void serialize_(dummy_serializable, serialization_context&) noexcept {}
static inline errc deserialize_(dummy_serializable, deserialization_context&) noexcept { return errc::ok; }
};*/
struct noop_serialization_traits
{
static inline std::size_t get_size_(...) noexcept { return 0; }
static inline void serialize_(...) noexcept {}
static inline errc deserialize_(...) noexcept { return errc::ok; }
};
template <>
struct serialization_traits<dummy_serializable, no_serialization_tag> : noop_serialization_traits
{
};
// Helpers for (de) serializing a set of fields
@@ -418,10 +285,30 @@ inline std::pair<error_code, std::uint8_t> deserialize_message_type(
deserialization_context& ctx
);
template <typename T>
struct get_serialization_tag : std::conditional<
is_fixed_size_int<T>(),
fixed_size_int_tag,
std::conditional_t<
std::is_floating_point<T>::value,
floating_point_tag,
std::conditional_t<
std::is_enum<T>::value,
enum_tag,
std::conditional_t<
is_struct_with_fields<T>(),
struct_tag,
no_serialization_tag
>
>
>
> {};
} // detail
} // mysql
} // boost
#include "boost/mysql/detail/protocol/impl/serialization.hpp"
#include "boost/mysql/detail/protocol/impl/serialization.ipp"
#endif

View File

@@ -2,10 +2,6 @@
#define TEST_SERIALIZATION_TEST_COMMON_HPP_
#include "boost/mysql/detail/protocol/serialization.hpp"
#include "boost/mysql/detail/protocol/common_messages.hpp"
#include "boost/mysql/detail/protocol/handshake_messages.hpp"
#include "boost/mysql/detail/protocol/query_messages.hpp"
#include "boost/mysql/detail/protocol/prepared_statement_messages.hpp"
#include "boost/mysql/detail/protocol/constants.hpp"
#include "boost/mysql/value.hpp"
#include <gtest/gtest.h>
@@ -49,7 +45,7 @@ bool equals_struct(const T& lhs, const T& rhs)
}
template <typename T>
std::enable_if_t<is_struct_with_fields<T>::value, bool>
std::enable_if_t<is_struct_with_fields<T>(), bool>
operator==(const T& lhs, const T& rhs)
{
return equals_struct<0>(lhs, rhs);
@@ -84,7 +80,7 @@ void print_struct(std::ostream& os, const T& value)
}
template <typename T>
std::enable_if_t<is_struct_with_fields<T>::value, std::ostream&>
std::enable_if_t<is_struct_with_fields<T>(), std::ostream&>
operator<<(std::ostream& os, const T& value)
{
os << boost::typeindex::type_id<T>().pretty_name() << "(\n";

View File

@@ -6,6 +6,10 @@
*/
#include "serialization_test_common.hpp"
#include "boost/mysql/detail/protocol/common_messages.hpp"
#include "boost/mysql/detail/protocol/handshake_messages.hpp"
#include "boost/mysql/detail/protocol/query_messages.hpp"
#include "boost/mysql/detail/protocol/prepared_statement_messages.hpp"
#include "test_common.hpp"
#include <forward_list>
#include <array>