raw pointers support, without polymorphism.

This commit is contained in:
fraillt
2017-10-27 08:14:01 +03:00
parent bdc24eb3c2
commit 5b1dc3bcfa
22 changed files with 1457 additions and 124 deletions

View File

@@ -24,6 +24,9 @@
#include <bitsery/adapter/stream.h>
#include <bitsery/adapter_writer.h>
#include <bitsery/adapter_reader.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <sstream>
//some helper types
@@ -33,21 +36,13 @@ using InputAdapter = bitsery::InputStreamAdapter ;
using Writer = bitsery::AdapterWriter<bitsery::OutputStreamAdapter, bitsery::DefaultConfig>;
using Reader = bitsery::AdapterReader<bitsery::InputStreamAdapter, bitsery::DefaultConfig>;
static constexpr size_t InternalBufferSize = 128;
using BufferedAdapterInternalBuffer = std::array<char, InternalBufferSize>;
using OutputBufferedAdapter = bitsery::BasicBufferedOutputStreamAdapter<char, std::char_traits<char>, BufferedAdapterInternalBuffer>;
using BufferedWriter = bitsery::AdapterWriter<OutputBufferedAdapter, bitsery::DefaultConfig>;
using testing::Eq;
TEST(AdapterIOStream, WrittenBytesCountReturns0) {
//setup data
uint8_t t1 = 111;
Stream buf{};
Writer w{{buf}};
w.writeBytes<1>(t1);
w.flush();
EXPECT_THAT(buf.str().size(), Eq(1));
EXPECT_THAT(w.writtenBytesCount(), Eq(0));
}
TEST(AdapterIOStream, CorrectlyReturnsIsCompletedSuccessfully) {
//setup data
uint8_t t1 = 111;
@@ -108,4 +103,59 @@ TEST(AdapterIOStream, WhenReadingMoreThanAvailableThenDataOverflow) {
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::DataOverflow));
}
template<typename T>
class AdapterBufferedOutputStream : public testing::Test {
public:
using Buffer = T;
using Adapter = bitsery::BasicBufferedOutputStreamAdapter<char, std::char_traits<char>, Buffer>;
using Writer = bitsery::AdapterWriter<Adapter, bitsery::DefaultConfig>;
static constexpr size_t InternalBufferSize = 128;
Stream stream{};
Writer writer{{stream, 128}};
};
using BufferedAdapterInternalBufferTypes = ::testing::Types<
std::vector<char>,
std::array<char, 128>,
std::string
>;
TYPED_TEST_CASE(AdapterBufferedOutputStream, BufferedAdapterInternalBufferTypes);
TYPED_TEST(AdapterBufferedOutputStream, WhenBufferOverflowThenWriteBufferAndRemainingDataToStream) {
uint8_t x{};
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
this->writer.template writeBytes<1>(x);
EXPECT_TRUE(this->stream.str().empty());
this->writer.template writeBytes<1>(x);
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
}
TYPED_TEST(AdapterBufferedOutputStream, WhenFlushThenWriteImmediately) {
uint8_t x{};
this->writer.template writeBytes<1>(x);
EXPECT_THAT(this->stream.str().size(), Eq(0));
this->writer.flush();
EXPECT_THAT(this->stream.str().size(), Eq(1));
this->writer.flush();
EXPECT_THAT(this->stream.str().size(), Eq(1));
}
TYPED_TEST(AdapterBufferedOutputStream, WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect) {
//create writer with half the internal buffer size
//for std::vector it should overflow, and for std::array it should have no effect
typename TestFixture::Writer w{{this->stream, TestFixture::InternalBufferSize / 2}};
uint8_t x{};
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
w.template writeBytes<1>(x);
static constexpr bool ShouldWriteToStream = bitsery::traits::ContainerTraits<typename TestFixture::Buffer>::isResizable;
EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream));
}

View File

@@ -0,0 +1,69 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
using testing::Eq;
template <typename Context>
using MySerializer = bitsery::BasicSerializer<Writer, Context>;
template <typename Context>
using MyDeserializer = bitsery::BasicDeserializer<Reader, Context>;
using SingleTypeContext = int;
using MultipleTypesContext = std::tuple<int, float, char>;
TEST(SerializationContext, WhenUsingContextThenReturnsUnderlyingPointerOrNull) {
Buffer buf{};
MySerializer<SingleTypeContext> ser1{buf, nullptr};
EXPECT_THAT(ser1.context(), ::testing::IsNull());
MySerializer<MultipleTypesContext> ser2{buf, nullptr};
EXPECT_THAT(ser2.context(), ::testing::IsNull());
SingleTypeContext sctx{};
MyDeserializer<SingleTypeContext> des1{InputAdapter{buf.begin(), 1}, &sctx};
EXPECT_THAT(des1.context(), Eq(&sctx));
MultipleTypesContext mctx{};
MyDeserializer<MultipleTypesContext> des2{InputAdapter{buf.begin(), 1}, &mctx};
EXPECT_THAT(des2.context(), Eq(&mctx));
}
TEST(SerializationContext, WhenContextIsTupleThenContextCastOverloadCastsToIndividualTupleTypes) {
Buffer buf{};
MySerializer<MultipleTypesContext> ser1{buf, nullptr};
EXPECT_THAT(ser1.context<int>(), ::testing::IsNull());
EXPECT_THAT(ser1.context<float>(), ::testing::IsNull());
EXPECT_THAT(ser1.context<char>(), ::testing::IsNull());
}
TEST(SerializationContext, WhenContextIsNotTupleThenContextCastOverloadReturnSameType) {
Buffer buf{};
SingleTypeContext ctx{};
MySerializer<SingleTypeContext> ser1{buf, &ctx};
EXPECT_THAT(ser1.context<SingleTypeContext>(), Eq(&ctx));
}

View File

@@ -45,7 +45,7 @@ struct DataV3 {
TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
auto& ser = ctx.createSerializer();
//session cannot be empty
ser.ext(int8_t{}, Growable{}, [&ser] (int8_t& v) {
@@ -59,7 +59,7 @@ TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSessionsDataOffset) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
constexpr size_t DATA_SIZE = 4;
@@ -89,7 +89,7 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSess
}
TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV3 data{19457,846, 498418};
ctx.createSerializer();
ctx.bw->beginSession();
@@ -121,7 +121,7 @@ TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV2 data{8454,987451};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -147,7 +147,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV3 data{8454,987451,54};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -174,7 +174,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
}
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV2 data{8454,987451};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -202,7 +202,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV2 data{8454,987451};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -238,7 +238,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV2 data{8454,987451};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -278,7 +278,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV3 data{8454,987451,54};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -334,7 +334,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
}
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV3 data{8454,987451,54};
ctx.createSerializer();
auto& bw = (*ctx.bw);
@@ -396,7 +396,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
}
TEST(SerializeExtensionGrowable, SessionsStartsAtEndOfSerialization) {
BasicSerializationContext<SessionsEnabledConfig> ctx;
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
DataV2 data{8454,987451};
ctx.createSerializer();
auto& bw = (*ctx.bw);

View File

@@ -0,0 +1,431 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/pointer.h>
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerObserver;
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerLinkingContext;
using testing::Eq;
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, PointerLinkingContext>;
class SerializeExtensionPointerSerialization: public testing::Test {
public:
//data used for serialization
int16_t d1{1597};
int16_t* pd1 = &d1;
MyEnumClass d2{MyEnumClass::E2};
MyEnumClass* pd2 = &d2;
MyStruct1 d3{184, 897};
MyStruct1* pd3 = &d3;
//data used for deserialization
int16_t r1{-84};
int16_t* pr1 = &r1;
MyEnumClass r2{MyEnumClass::E4};
MyEnumClass* pr2 = &r2;
MyStruct1 r3{-4984, -14597};
MyStruct1* pr3 = &r3;
//null pointers
int16_t* p1null = nullptr;
MyEnumClass* p2null = nullptr;
MyStruct1* p3null = nullptr;
PointerLinkingContext plctx1{};
SerContext sctx1{};
typename SerContext::TSerializer& createSerializer() {
return sctx1.createSerializer(&plctx1);
}
bool isPointerContextValid() {
return plctx1.isValid();
}
};
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
MyStruct1* data = nullptr;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).ext(data, PointerOwner{});
sctx1.createDeserializer(&plctx1).ext(data, PointerOwner{});
//linking context in tuple
using ContextInTuple = std::tuple<int, PointerLinkingContext, float, char>;
ContextInTuple plctx2(0, PointerLinkingContext{}, 0.0f, 'a');
BasicSerializationContext<SessionsEnabledConfig, ContextInTuple> sctx2;
sctx2.createSerializer(&plctx2).ext(data, PointerObserver{});
sctx2.createDeserializer(&plctx2).ext(data, PointerObserver{});
}
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
MyStruct1 data{};
//pretend that this is shared ptr
MyStruct1* sharedPtr = &data;
//linking context
PointerLinkingContext plctx1{};
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
EXPECT_THAT(plctx1.createId(sharedPtr, bitsery::ext::details_pointer::PointerOwnershipType::Shared), Eq(1));
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(p1null, PointerObserver{});
ser.ext(p3null, PointerOwner{});
ser.ext(p3null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
#ifndef NDEBUG
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullThenAssert) {
MyStruct1* data = nullptr;
//linking context
PointerLinkingContext plctx1{};
SerContext sctx1;
EXPECT_DEATH(sctx1.createSerializer(nullptr).ext(data, PointerOwner{}), "");
EXPECT_DEATH(sctx1.createDeserializer(nullptr).ext(data, PointerObserver{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
ser.ext2b(p1null, PointerOwner{});
//dublicating pointer
EXPECT_DEATH(ser.ext2b(pd1, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(d3, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext(pd3, PointerOwner{}), "");
}
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext4b(d2, ReferencedByPointer{});
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
}
#endif
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(pd2, PointerOwner{});//now d2 is owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
auto& ser1 = createSerializer();
ser1.ext2b(p1null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
EXPECT_THAT(plctx1.isValid(), Eq(false));
ser1.ext4b(d2, ReferencedByPointer{});//now d2 is marked by marked as owning pointer
ser1.ext4b(pd2, PointerObserver{});//points to d2, but this time d2 has owner
ser1.ext(p3null, PointerObserver{});
EXPECT_THAT(plctx1.isValid(), Eq(true));
}
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
auto& ser1 = createSerializer();
ser1.ext(p3null, PointerOwner{});
ser1.ext2b(p1null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2));
size_t res;
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext2b(p1null, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
size_t res;
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(1));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(2));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(2));
bitsery::details::readSize(*sctx1.br, res, 10000u);
EXPECT_THAT(res, Eq(0));
}
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
auto& ser1 = createSerializer();
ser1.ext2b(pd1, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
ser1.ext4b(pd2, PointerObserver{});
sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3));
}
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
auto& ser1 = createSerializer();
ser1.ext2b(d1, ReferencedByPointer{});
ser1.ext4b(d2, ReferencedByPointer{});
ser1.ext4b(pd2, PointerObserver{});
auto& des = sctx1.createDeserializer();
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3+6));
size_t id{};
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(1));
des.value2b(r1);
EXPECT_THAT(r1, Eq(d1));
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(2));
des.value4b(r2);
EXPECT_THAT(r2, Eq(d2));
bitsery::details::readSize(*sctx1.br, id, 10000u);
EXPECT_THAT(id, Eq(2));
}
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
auto& ser1 = createSerializer();
ser1.ext4b(pd2, PointerOwner{});
ser1.ext(pd3, PointerOwner{});
auto& des1 = sctx1.createDeserializer();
//2x ids + int32_t + MyStruct1
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE ));
size_t id;
bitsery::details::readSize(*sctx1.br, id, 10000u);
des1.value4b(r2);
EXPECT_THAT(r2, Eq(*pd2));
bitsery::details::readSize(*sctx1.br, id, 10000u);
des1.object(r3);
EXPECT_THAT(r3, Eq(*pd3));
}
class SerializeExtensionPointerDeserialization: public SerializeExtensionPointerSerialization {
public:
typename SerContext::TSerializer& createSerializer() {
return sctx1.createSerializer(&plctx1);
}
typename SerContext::TDeserializer& createDeserializer() {
return sctx1.createDeserializer(&plctx1);
}
};
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
auto& ser = createSerializer();
ser.ext2b(d1, ReferencedByPointer{});
ser.ext4b(d2, ReferencedByPointer{});
ser.ext(d3, ReferencedByPointer{});
auto& des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
des.ext4b(r2, ReferencedByPointer{});
des.ext(r3, ReferencedByPointer{});
EXPECT_THAT(r1, Eq(d1));
EXPECT_THAT(r2, Eq(d2));
EXPECT_THAT(r3, Eq(d3));
}
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsZeroPointerIdThenInvalidPointerError) {
auto& ser = createSerializer();
bitsery::details::writeSize(*sctx1.bw, 0u);
ser.ext2b(d1, ReferencedByPointer{});
auto& des = createDeserializer();
des.ext2b(r1, ReferencedByPointer{});
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
auto& ser = createSerializer();
ser.ext2b(pd1, PointerOwner{});
ser.ext4b(pd2, PointerOwner{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
des.ext2b(p1null, PointerOwner{});
des.ext4b(p2null, PointerOwner{});
des.ext(p3null, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(p1null, ::testing::NotNull());
EXPECT_THAT(p2null, ::testing::NotNull());
EXPECT_THAT(p3null, ::testing::NotNull());
EXPECT_THAT(*p1null, Eq(*pd1));
EXPECT_THAT(*p2null, Eq(*pd2));
EXPECT_THAT(*p3null, Eq(*pd3));
}
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
auto& ser = createSerializer();
ser.ext2b(p1null, PointerOwner{});
ser.ext4b(p2null, PointerOwner{});
ser.ext(p3null, PointerOwner{});
auto& des = createDeserializer();
//pr cannot link to local variables, need to allocate them separately
pr1 = new int16_t{};
pr2 = new MyEnumClass{};
pr3 = new MyStruct1{3,4};
des.ext2b(pr1, PointerOwner{});
des.ext4b(pr2, PointerOwner{});
des.ext(pr3, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
EXPECT_THAT(pr1, ::testing::IsNull());
EXPECT_THAT(pr2, ::testing::IsNull());
EXPECT_THAT(pr3, ::testing::IsNull());
}
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
auto& ser = createSerializer();
//first owner, than observer
ser.ext4b(d2, ReferencedByPointer{});
ser.ext2b(p1null, PointerObserver{});
ser.ext4b(pd2, PointerObserver{});
//first observer, than owner
ser.ext(pd3, PointerObserver{});
ser.ext(pd3, PointerOwner{});
auto& des = createDeserializer();
des.ext4b(r2, ReferencedByPointer{});
des.ext2b(pr1, PointerObserver{});
des.ext4b(p2null, PointerObserver{});
des.ext(pr3, PointerObserver{});
des.ext(pr3, PointerOwner{});
EXPECT_THAT(isPointerContextValid(), Eq(true));
//serialize null, override non-null
EXPECT_THAT(pr1, Eq(p1null));
//serialize non-null, override null
EXPECT_THAT(*p2null, Eq(*pd2));
EXPECT_THAT(p2null, Eq(&r2));
//serialize non-null override non-null
EXPECT_THAT(*pr3, Eq(*pd3));
EXPECT_THAT(pr3, Eq(&r3));
}
struct Test1Data {
std::vector<MyStruct1> vdata;
std::vector<MyStruct1*> vptr;
MyStruct1 o1;
MyStruct1* po1;
int32_t i1;
int32_t* pi1;
template <typename S>
void serialize(S& s) {
//set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [&s](MyStruct1& d){
s.ext(d, ReferencedByPointer{});
});
//contains non owning pointers
//
//IMPORTANT !!!
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
//
s.container(vptr, 100, [&s](MyStruct1* (&d)){
s.ext(d, PointerObserver{});
});
//just a regular fields
s.object(o1);
s.value4b(i1);
//observer
s.ext(po1, PointerObserver{});
//owner
s.ext4b(pi1, PointerOwner{});
}
};
TEST(SerializeExtensionPointer, IntegrationTest) {
Test1Data data{};
data.vdata.push_back({165,-45});
data.vdata.push_back({7895,-1576});
data.vdata.push_back({5987,-798});
//container of non owning pointers (observers)
data.vptr.push_back(nullptr);
data.vptr.push_back(std::addressof(data.vdata[0]));
data.vptr.push_back(std::addressof(data.vdata[2]));
//regular fields
data.o1 = MyStruct1{145,948};
data.i1 = 945415;
//observer
data.po1 = std::addressof(data.vdata[1]);
//owning pointer
data.pi1 = new int32_t{};
Test1Data res{};
PointerLinkingContext plctx1{};
SerContext sctx1;
sctx1.createSerializer(&plctx1).object(data);
sctx1.createDeserializer(&plctx1).object(res);
EXPECT_THAT(plctx1.isValid(), Eq(true));
//check regular fields
EXPECT_THAT(res.i1, Eq(data.i1));
EXPECT_THAT(res.o1, Eq(data.o1));
//check data container
EXPECT_THAT(res.vdata, ::testing::ContainerEq(data.vdata));
//check owning pointers
EXPECT_THAT(*res.pi1, Eq(*data.pi1));
EXPECT_THAT(res.pi1, ::testing::Ne(data.pi1));
//check if observers points to correct data
EXPECT_THAT(res.po1, Eq(std::addressof(res.vdata[1])));
EXPECT_THAT(res.vptr[0], ::testing::IsNull());
EXPECT_THAT(res.vptr[1], Eq(std::addressof(res.vdata[0])));
EXPECT_THAT(res.vptr[2], Eq(std::addressof(res.vdata[2])));
//free owning raw pointers
delete data.pi1;
delete res.pi1;
}

View File

@@ -38,8 +38,8 @@ struct MyStruct1 {
MyStruct1() : MyStruct1{0, 0} {}
int i1;
int i2;
int32_t i1;
int32_t i2;
bool operator==(const MyStruct1 &rhs) const {
return i1 == rhs.i1 && i2 == rhs.i2;
@@ -97,31 +97,33 @@ using Writer = bitsery::AdapterWriter<OutputAdapter, bitsery::DefaultConfig>;
using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
template <typename Config = bitsery::DefaultConfig>
template <typename Config, typename Context>
class BasicSerializationContext {
public:
using TWriter = bitsery::AdapterWriter<OutputAdapter, Config>;
using TReader = bitsery::AdapterReader<InputAdapter, Config>;
using TSerializer = bitsery::BasicSerializer<TWriter, Context>;
using TDeserializer = bitsery::BasicDeserializer<TReader, Context>;
Buffer buf{};
std::unique_ptr<bitsery::BasicSerializer<TWriter>> ser{};
std::unique_ptr<bitsery::BasicDeserializer<TReader>> des{};
std::unique_ptr<TSerializer> ser{};
std::unique_ptr<bitsery::BasicDeserializer<TReader, Context>> des{};
TWriter* bw{};
TReader* br{};
bitsery::BasicSerializer<TWriter>& createSerializer() {
TSerializer& createSerializer(Context* ctx = nullptr) {
if (!ser) {
ser = std::unique_ptr<bitsery::BasicSerializer<TWriter>>(new bitsery::BasicSerializer<TWriter>(OutputAdapter{buf}));
ser = std::unique_ptr<TSerializer>(new TSerializer(OutputAdapter{buf}, ctx));
bw = &bitsery::AdapterAccess::getWriter(*ser);
}
return *ser;
};
bitsery::BasicDeserializer<bitsery::AdapterReader<InputAdapter, Config>>& createDeserializer() {
TDeserializer & createDeserializer(Context* ctx = nullptr) {
bw->flush();
if (!des) {
des = std::unique_ptr<bitsery::BasicDeserializer<TReader>>(
new bitsery::BasicDeserializer<TReader>(InputAdapter{buf.begin(), bw->writtenBytesCount()}));
des = std::unique_ptr<TDeserializer>(
new TDeserializer(InputAdapter{buf.begin(), bw->writtenBytesCount()}, ctx));
br = &bitsery::AdapterAccess::getReader(*des);
}
return *des;
@@ -144,6 +146,6 @@ public:
};
//helper type
using SerializationContext = BasicSerializationContext<bitsery::DefaultConfig>;
using SerializationContext = BasicSerializationContext<bitsery::DefaultConfig, void>;
#endif //BITSERY_SERIALIZER_TEST_UTILS_H