Skip to content

Commit

Permalink
Optimize byte serializable types further.
Browse files Browse the repository at this point in the history
  • Loading branch information
eyalz800 committed Dec 19, 2021
1 parent e9a9979 commit e09f52f
Show file tree
Hide file tree
Showing 3 changed files with 344 additions and 56 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,39 @@ auto [data, in] = zpp::bits::data_in();
auto [data, out] = zpp::bits::data_out();
```
* If your object and its subobjects recursively can just be byte copied, i.e don't have references or pointers or non
trivially copyable subobjects (more accurately, your object fits into the bit cast constexpr requirements), then you don't need to specify the number of members and
serializing your object becomes a `memcpy` operation. Unless of course you define an explicit serialization function in which
case the members are serialized one by one separately.
```cpp
struct point
{
int x;
int y;
};
auto [data, out] = zpp::bits::data_out();
out(point{1337, 1338});
```
The above is serializable/deserializable without any modification by using a `memcpy` operation.
This has the advantage of better performance most of the times, but the disadvantage is that the format becomes less portable
due to potential padding bytes.

To avoid this behavior and serialize members one by one you can either define the explicit serialization function or
use `zpp::bits::explicit_members` and provide the number of members:
```cpp
struct requires_padding
{
using serialize = zpp::bits::explicit_members<2>;

std::byte x;
int y;
};
```
If you are using automatic member detection as per `ZPP_BITS_AUTODETECT_MEMBERS_MODE=1`, you may leave the
angle brackets empty as in `zpp::bits::explicit_members<>`.

* Archives can be constructed from either one of the byte types:
```cpp
// Either one of these work with the below.
Expand Down
201 changes: 201 additions & 0 deletions test/src/test_byte_serializable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#include "test.h"
#include <compare>

namespace test_byte_serializable
{

struct integers
{
int x;
int y;
};

struct explicit_integers
{
using serialize = zpp::bits::explicit_members<2>;
int x;
int y;
};

struct explicit_integers_auto
{
using serialize = zpp::bits::explicit_members<>;
int x;
int y;
};

struct ref
{
int & x;
int y;
};

struct pointer
{
int * x;
int y;
};

struct array
{
int x[5];
int y;
};

struct stdarray
{
std::array<int, 5> x;
int y;
};

struct stdarray_array
{
std::array<int, 5> x[5];
int y;
};

struct vector
{
std::vector<char> x;
int y;
};

static_assert(zpp::bits::concepts::byte_serializable<integers>);
static_assert(!zpp::bits::concepts::byte_serializable<explicit_integers>);
static_assert(
!zpp::bits::concepts::byte_serializable<explicit_integers_auto>);
static_assert(!zpp::bits::concepts::byte_serializable<ref>);
static_assert(!zpp::bits::concepts::byte_serializable<pointer>);
static_assert(zpp::bits::concepts::byte_serializable<array>);
static_assert(zpp::bits::concepts::byte_serializable<stdarray>);
static_assert(zpp::bits::concepts::byte_serializable<stdarray_array>);
static_assert(!zpp::bits::concepts::byte_serializable<vector>);
static_assert(!zpp::bits::concepts::byte_serializable<std::vector<char>>);
static_assert(!zpp::bits::concepts::byte_serializable<std::string>);
static_assert(!zpp::bits::concepts::byte_serializable<std::string_view>);
static_assert(!zpp::bits::concepts::byte_serializable<std::span<char>>);
static_assert(zpp::bits::concepts::byte_serializable<std::array<char, 2>>);

class inaccessible
{
public:
inaccessible() = default;
inaccessible(int x, int y) : x(x), y(y)
{
}

std::strong_ordering operator<=>(const inaccessible & other) const =
default;

public:
int x{};
int y{};
};

TEST(byte_serializable, inaccessible)
{
static_assert(zpp::bits::concepts::byte_serializable<inaccessible>);

auto [data, in, out] = zpp::bits::data_in_out();
out(inaccessible{1337, 1338}).or_throw();

inaccessible s;
in(s).or_throw();

EXPECT_EQ(s, (inaccessible{1337, 1338}));
}

struct requires_padding
{
std::byte b{};
std::int32_t i32{};
};

TEST(byte_serializable, requires_padding)
{
static_assert(
zpp::bits::concepts::byte_serializable<requires_padding>);
static_assert(sizeof(requires_padding) > 5);

auto [data, in, out] = zpp::bits::data_in_out();
out(requires_padding{std::byte{0x25}, 0x1337}).or_throw();

EXPECT_EQ(data.size(), sizeof(requires_padding));
EXPECT_NE(hexlify(data),
"25"
"37130000");

requires_padding s;
in(s).or_throw();
EXPECT_EQ(in.position(), sizeof(requires_padding));
EXPECT_EQ(s.b, std::byte{0x25});
EXPECT_EQ(s.i32, 0x1337);
}

struct explicit_requires_padding
{
using serialize = zpp::bits::explicit_members<2>;
std::byte b{};
std::int32_t i32{};
};

TEST(byte_serializable, explicit_requires_padding)
{
static_assert(!zpp::bits::concepts::byte_serializable<
explicit_requires_padding>);
static_assert(sizeof(requires_padding) ==
sizeof(explicit_requires_padding));
static_assert(sizeof(explicit_requires_padding) > 5);

auto [data, in, out] = zpp::bits::data_in_out();
out(explicit_requires_padding{std::byte{0x25}, 0x1337}).or_throw();

EXPECT_EQ(data.size(), std::size_t{5});
EXPECT_EQ(hexlify(data),
"25"
"37130000");

explicit_requires_padding s;
in(s).or_throw();
EXPECT_EQ(in.position(), std::size_t{5});
EXPECT_EQ(s.b, std::byte{0x25});
EXPECT_EQ(s.i32, 0x1337);
}

#if ZPP_BITS_AUTODETECT_MEMBERS_MODE > 0
struct explicit_requires_padding_auto
{
using serialize = zpp::bits::explicit_members<>;
std::byte b{};
std::int32_t i32{};
};

static_assert(sizeof(requires_padding) ==
sizeof(explicit_requires_padding_auto));

TEST(byte_serializable, explicit_requires_padding_auto)
{
static_assert(!zpp::bits::concepts::byte_serializable<
explicit_requires_padding_auto>);
static_assert(sizeof(requires_padding) ==
sizeof(explicit_requires_padding_auto));
static_assert(sizeof(explicit_requires_padding_auto) > 5);

auto [data, in, out] = zpp::bits::data_in_out();
out(explicit_requires_padding_auto{std::byte{0x25}, 0x1337})
.or_throw();

EXPECT_EQ(data.size(), std::size_t{5});
EXPECT_EQ(hexlify(data),
"25"
"37130000");

explicit_requires_padding_auto s;
in(s).or_throw();
EXPECT_EQ(in.position(), std::size_t{5});
EXPECT_EQ(s.b, std::byte{0x25});
EXPECT_EQ(s.i32, 0x1337);
}
#endif

} // namespace test_byte_serializable
Loading

0 comments on commit e09f52f

Please sign in to comment.