serialise: add a simple binary serialisation framework
This commit is contained in:
parent
dc46dc7c91
commit
c3866ef632
@ -550,6 +550,9 @@ list (
|
|||||||
registrar.hpp
|
registrar.hpp
|
||||||
roots/bisection.hpp
|
roots/bisection.hpp
|
||||||
scoped.hpp
|
scoped.hpp
|
||||||
|
serialise/converter.hpp
|
||||||
|
serialise/ops.hpp
|
||||||
|
serialise/std.hpp
|
||||||
set/dset.cpp
|
set/dset.cpp
|
||||||
set/dset.hpp
|
set/dset.hpp
|
||||||
signal.cpp
|
signal.cpp
|
||||||
@ -782,6 +785,7 @@ if (TESTS)
|
|||||||
registrar
|
registrar
|
||||||
roots/bisection
|
roots/bisection
|
||||||
scoped
|
scoped
|
||||||
|
serialise
|
||||||
set/dset
|
set/dset
|
||||||
signal
|
signal
|
||||||
singleton
|
singleton
|
||||||
|
61
serialise/converter.hpp
Normal file
61
serialise/converter.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2022, Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cruft/util/view.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cruft::serialise {
|
||||||
|
template <typename ValueT>
|
||||||
|
struct converter {
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
ValueT const &src
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
ValueT
|
||||||
|
from_bytes (cruft::view<std::byte*> &src);
|
||||||
|
|
||||||
|
static std::size_t size (ValueT const&);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueT> struct converter<ValueT const> : public converter<ValueT> {};
|
||||||
|
template <typename ValueT> struct converter<ValueT &> : public converter<ValueT> {};
|
||||||
|
|
||||||
|
template <typename ValueT>
|
||||||
|
requires (std::is_trivial_v<ValueT>)
|
||||||
|
struct converter<ValueT> {
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
ValueT const &src
|
||||||
|
) {
|
||||||
|
if (dst.size () < sizeof (ValueT))
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
|
||||||
|
return write (dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
ValueT
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
return read<ValueT> (src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static std::size_t size (ValueT const&) { return sizeof (ValueT); }
|
||||||
|
};
|
||||||
|
}
|
50
serialise/ops.hpp
Normal file
50
serialise/ops.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2022, Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./converter.hpp"
|
||||||
|
|
||||||
|
#include <cruft/util/view.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cruft::serialise {
|
||||||
|
template <typename ValueT>
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (cruft::view<std::byte*> dst, ValueT const &src)
|
||||||
|
{
|
||||||
|
return converter<ValueT>::to_bytes (dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename HeadT, typename ...TailT>
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (cruft::view<std::byte*> dst, HeadT const &head, TailT&& ...tail)
|
||||||
|
{
|
||||||
|
dst = to_bytes (dst, head);
|
||||||
|
if constexpr (sizeof... (TailT))
|
||||||
|
dst = to_bytes (dst, std::forward<TailT> (tail)...);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ValueT>
|
||||||
|
ValueT
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
return converter<ValueT>::from_bytes (src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ...ArgsT>
|
||||||
|
std::size_t size (ArgsT &&...args)
|
||||||
|
{
|
||||||
|
return (converter<ArgsT>::size (args) + ... + 0uz);
|
||||||
|
}
|
||||||
|
}
|
193
serialise/std.hpp
Normal file
193
serialise/std.hpp
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* Copyright 2022, Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./converter.hpp"
|
||||||
|
#include "./ops.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ElementT>
|
||||||
|
struct cruft::serialise::converter<std::vector<ElementT>> {
|
||||||
|
using value_type = std::vector<ElementT>;
|
||||||
|
|
||||||
|
static std::size_t
|
||||||
|
size (value_type const &val)
|
||||||
|
{
|
||||||
|
std::size_t accum = sizeof (typename value_type::size_type);
|
||||||
|
for (auto const &i: val)
|
||||||
|
accum += converter<ElementT>::size (i);
|
||||||
|
return accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
value_type const &src
|
||||||
|
) {
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, src.size ());
|
||||||
|
|
||||||
|
for (auto const &i: src)
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, i);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
value_type
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
auto const len = ::cruft::serialise::from_bytes<typename std::vector<ElementT>::size_type> (src);
|
||||||
|
|
||||||
|
std::vector<ElementT> res;
|
||||||
|
res.reserve (len);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i != len; ++i)
|
||||||
|
res.push_back (::cruft::serialise::from_bytes<ElementT> (src));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <>
|
||||||
|
struct cruft::serialise::converter<std::string> {
|
||||||
|
using value_type = std::string;
|
||||||
|
|
||||||
|
static std::size_t
|
||||||
|
size (value_type const &val)
|
||||||
|
{
|
||||||
|
return sizeof (value_type::size_type) + sizeof (value_type::value_type) * val.size ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
value_type const &src
|
||||||
|
) {
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, src.size ());
|
||||||
|
for (auto const &i: src)
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, i);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
value_type
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res.resize (::cruft::serialise::converter<std::string::size_type>::from_bytes (src));
|
||||||
|
for (auto &i: res)
|
||||||
|
i = ::cruft::serialise::converter<std::string::value_type>::from_bytes (src);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct cruft::serialise::converter<std::pair<A, B>> {
|
||||||
|
using value_type = std::pair<A, B>;
|
||||||
|
|
||||||
|
static std::size_t
|
||||||
|
size (value_type const &val)
|
||||||
|
{
|
||||||
|
return converter<A>::size (val.first) + converter<B>::size (val.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
value_type const &src
|
||||||
|
) {
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, src.first);
|
||||||
|
dst = ::cruft::serialise::to_bytes (dst, src.second);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
value_type
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
cruft::serialise::converter<A>::from_bytes (src),
|
||||||
|
cruft::serialise::converter<B>::from_bytes (src),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ...ElementT>
|
||||||
|
struct cruft::serialise::converter<std::tuple<ElementT...>> {
|
||||||
|
using value_type = std::tuple<ElementT...>;
|
||||||
|
|
||||||
|
static std::size_t
|
||||||
|
size (value_type const &val)
|
||||||
|
{
|
||||||
|
return size (val, std::make_index_sequence<sizeof ...(ElementT)> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
value_type const &src
|
||||||
|
) {
|
||||||
|
return to_bytes (dst, src, std::make_index_sequence<sizeof... (ElementT)> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
value_type
|
||||||
|
from_bytes (cruft::view<std::byte*> &src)
|
||||||
|
{
|
||||||
|
return from_bytes (src, std::make_index_sequence<sizeof... (ElementT)> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <std::size_t ...Index>
|
||||||
|
static std::size_t
|
||||||
|
size (value_type const &val, std::index_sequence<Index...> const &)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
converter<
|
||||||
|
std::tuple_element_t<Index, value_type>
|
||||||
|
>::size (
|
||||||
|
std::get<Index> (val)
|
||||||
|
) + ... + 0uz
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <std::size_t ...IndexV>
|
||||||
|
static
|
||||||
|
cruft::view<std::byte*>
|
||||||
|
to_bytes [[nodiscard]] (
|
||||||
|
cruft::view<std::byte*> dst,
|
||||||
|
value_type const &src,
|
||||||
|
std::index_sequence<IndexV...> const&
|
||||||
|
) {
|
||||||
|
return ::cruft::serialise::to_bytes (dst, std::get<IndexV> (src)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <std::size_t ...IndexV>
|
||||||
|
static
|
||||||
|
value_type
|
||||||
|
from_bytes (cruft::view<std::byte*> &src, std::index_sequence<IndexV...> const&)
|
||||||
|
{
|
||||||
|
return std::tuple {
|
||||||
|
::cruft::serialise::from_bytes<ElementT> (src)...,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
76
test/serialise.cpp
Normal file
76
test/serialise.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include <cruft/util/tap.hpp>
|
||||||
|
|
||||||
|
#include <cruft/util/serialise/ops.hpp>
|
||||||
|
#include <cruft/util/serialise/std.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
static void
|
||||||
|
test_scalars (cruft::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
std::vector<std::byte> store (sizeof (f32) + sizeof (i16) + sizeof (char));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto remain = cruft::serialise::to_bytes (store, 2.f, i16 (3), char (4));
|
||||||
|
tap.expect (remain.empty (), "scalar serialise: no remainder");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cruft::view remain (store);
|
||||||
|
tap.expect_eq (cruft::serialise::from_bytes<f32> (remain), 2.f, "scalar extract: f32");
|
||||||
|
tap.expect_eq (cruft::serialise::from_bytes<i16> (remain), 3, "scalar extract: i16");
|
||||||
|
tap.expect_eq (cruft::serialise::from_bytes<char> (remain), 4, "scalar extract: char");
|
||||||
|
tap.expect (remain.empty (), "scalar extract: empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ValueT>
|
||||||
|
static void
|
||||||
|
test_roundtrip (cruft::TAP::logger &tap, char const *label, ValueT const &val)
|
||||||
|
{
|
||||||
|
std::vector<std::byte> raw (cruft::serialise::size (val));
|
||||||
|
{
|
||||||
|
cruft::view remain (raw);
|
||||||
|
remain = cruft::serialise::to_bytes (remain, val);
|
||||||
|
tap.expect (remain.empty (), "round_trip {}: serialise", label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
cruft::view remain (raw);
|
||||||
|
auto const extracted = cruft::serialise::from_bytes<ValueT> (remain);
|
||||||
|
tap.expect_eq (extracted, val, "round_trip {}: extract", label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
static void
|
||||||
|
test_stdlib (cruft::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
test_roundtrip (tap, "string", std::string ("this is a string"));
|
||||||
|
test_roundtrip (tap, "pair", std::pair<int, bool> (1, false));
|
||||||
|
test_roundtrip (tap, "vector", std::vector<i16> { 1, 2, 3 });
|
||||||
|
test_roundtrip (
|
||||||
|
tap,
|
||||||
|
"tuple",
|
||||||
|
std::tuple (
|
||||||
|
std::string ("this is a string"),
|
||||||
|
std::pair<int, bool> { 1, false },
|
||||||
|
std::vector<i16> { 1, 2, 3 }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
int main ()
|
||||||
|
{
|
||||||
|
cruft::TAP::logger tap;
|
||||||
|
|
||||||
|
test_scalars (tap);
|
||||||
|
test_stdlib (tap);
|
||||||
|
|
||||||
|
return tap.status ();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user