From 8607338bfbaad2904bcf74d63d587d14f2d05ec7 Mon Sep 17 00:00:00 2001 From: fraillt Date: Fri, 10 Feb 2017 16:09:59 +0200 Subject: [PATCH] basic functionality is tested and is can be used --- README.md | 100 ++++++++++++++++++++- examples/basic_usage.cpp | 11 ++- include/BufferReader.h | 79 +++++++++------- include/BufferWriter.h | 92 ++++++++++--------- include/Common.h | 6 +- include/DeltaDeserializer.h | 27 +++--- include/DeltaSerializer.h | 26 +++--- include/Deserializer.h | 29 ++++-- include/Serializer.h | 31 ++++--- tests/BufferBitsOpTests.cpp | 76 ++++++++++++---- tests/SerializationContainerTests.cpp | 2 + tests/SerializationFixedSizeArrayTests.cpp | 68 +++++++++++++- tests/SerializationTestUtils.h | 6 +- tests/SerializationTextTests.cpp | 33 +++---- tests/SerializerObjectsTests.cpp | 2 +- 15 files changed, 431 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index 1faad79..de93181 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,98 @@ -# Bitsery # -Header only C++ binary serialization library. +# Bitsery + +Header only C++ binary serialization library. +It is designed around the networking requirements for multiplayer real-time fast paced games as first person shooters. +All cross-platform requirements are enforced at compile time, so serialized data do not store any run-time type information and is as small as possible. + +## Status + +**bitsery** is in pre-release state and is looking for your feedback. +It has basic features, serialize arithmetic types, enums, containers and text, but is missing any advanced compression functions like delta changes serialization, entropy encoding, floating point compression, geometry compression, etc... +> Current version do not handle Big/Little Endianness. + +## Example +```cpp +#include +#include +#include "BufferReader.h" +#include "BufferWriter.h" +#include "Serializer.h" +#include "Deserializer.h" + +enum class MyEnum { + V1,V2,V3 +}; + +struct MyStruct { + int i; + MyEnum e; + std::vector fs; +}; + +//define how object should be serialized +SERIALIZE(MyStruct) { + return s. + value(o.i). + value(o.e). + container(o.fs); +} + +void print(const char* msg, const MyStruct& v) { + std::cout << msg << std::endl; + std::cout << "i:" << v.i << std::endl; + std::cout << "e:" << (int)v.e << std::endl; + std::cout << "fs:"; + for (auto p:v.fs) + std::cout << '\t' << p; + std::cout << std::endl << std::endl; +} + +int main() { + //set some random data + MyStruct data{}; + data.e = MyEnum::V2; + data.i = 48465; + data.fs.resize(4); + float tmp = 4253; + for (auto& v: data.fs) { + tmp /=2; + v = tmp; + } + + //create serializer + //1) create buffer to store data + std::vector buffer; + //2) create buffer writer that is able to write bytes or bits to buffer + BufferWriter bw{buffer}; + //3) create serializer + Serializer ser{bw}; + + //call serialize function + serialize(ser, data); + + //flush to buffer + bw.flush(); + + MyStruct result{}; + + //create deserializer + //1) create buffer reader + BufferReader br{buffer}; + //2) create deserializer + Deserializer des{br}; + + //call same function with different arguments + serialize(des, result); + + //print results + print("initial data", data); + print("result", result); +} +``` + +## Platforms + +This library was tested on +* Windows: Visual Studio 2015 +* Linux: GCC 5.4, GCC 6.2 -Currently it is not usable diff --git a/examples/basic_usage.cpp b/examples/basic_usage.cpp index 1f2dca2..cec9417 100644 --- a/examples/basic_usage.cpp +++ b/examples/basic_usage.cpp @@ -18,6 +18,7 @@ struct MyStruct { std::vector fs; }; +//define how object should be serialized SERIALIZE(MyStruct) { return s. value(o.i). @@ -48,17 +49,25 @@ int main() { } //create serializer + //1) create buffer to store data std::vector buffer; + //2) create buffer writer that is able to write bytes or bits to buffer BufferWriter bw{buffer}; + //3) create serializer Serializer ser{bw}; + //call serialize function serialize(ser, data); - //this is required if using bit operations + + //flush to buffer bw.flush(); MyStruct result{}; + //create deserializer + //1) create buffer reader BufferReader br{buffer}; + //2) create deserializer Deserializer des{br}; //call same function with different arguments diff --git a/include/BufferReader.h b/include/BufferReader.h index 2eec00c..e35f36b 100644 --- a/include/BufferReader.h +++ b/include/BufferReader.h @@ -7,6 +7,7 @@ #include "Common.h" +#include #include struct BufferReader { @@ -21,9 +22,9 @@ struct BufferReader { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); using UT = typename std::make_unsigned::type; - return m_scratch - ? readBits(reinterpret_cast(v)) - : directRead(&v, 1); + return !m_scratch + ? directRead(&v, 1) + : readBits(reinterpret_cast(v), BITS_SIZE); } template @@ -31,28 +32,59 @@ struct BufferReader { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (m_scratchBits) { - //todo implement -// using UT = typename std::make_unsigned::type; -// writeBits(reinterpret_cast(v)); - } else { + if (!m_scratchBits) { return directRead(buf, count); + } else { + using UT = typename std::make_unsigned::type; + //todo improve implementation + const auto end = buf + count; + for (auto it = buf; it != end; ++it) { + if (!readBits(reinterpret_cast(*it), BITS_SIZE)) + return false; + } } return true; } - template - bool readBits(T& v) { + template + bool readBits(T& v, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - static_assert(SIZE > 0 && SIZE <= BITS_SIZE, ""); + assert(bitsCount <= BITS_SIZE); - const auto bytesRequired = SIZE > m_scratchBits - ? ((SIZE - 1 - m_scratchBits) >> 3) + 1u + const auto bytesRequired = bitsCount > m_scratchBits + ? ((bitsCount - 1 - m_scratchBits) >> 3) + 1u : 0u; if (static_cast(std::distance(_pos, std::end(_buf))) < bytesRequired ) return false; - readBitsInternal(v, SIZE); + readBitsInternal(v, bitsCount); + return true; + } + + bool align() { + if ( m_scratchBits ) { + SCRATCH_TYPE tmp{}; + readBitsInternal(tmp, BITS_SIZE - m_scratchBits); + return tmp == 0; + } + return true; + } + + bool isCompleted() const { + return _pos == std::end(_buf); + } + +private: + const std::vector& _buf; + decltype(std::begin(_buf)) _pos; + template + bool directRead(T* v, size_t count) { + static_assert(!std::is_const::value, ""); + const auto bytesCount = sizeof(T) * count; + if (static_cast(std::distance(_pos, std::end(_buf))) < bytesCount) + return false; + std::copy_n(_pos, bytesCount, reinterpret_cast(v)); + std::advance(_pos, bytesCount); return true; } @@ -80,25 +112,6 @@ struct BufferReader { v = res; } - bool isCompleted() const { - return _pos == std::end(_buf); - } - -private: - const std::vector& _buf; - decltype(std::begin(_buf)) _pos; - template - bool directRead(T* v, size_t count) { - static_assert(!std::is_const::value, ""); - const auto bytesCount = sizeof(T) * count; - if (static_cast(std::distance(_pos, std::end(_buf))) < bytesCount) - return false; - std::copy_n(_pos, bytesCount, reinterpret_cast(v)); - std::advance(_pos, bytesCount); - return true; - } - - using SCRATCH_TYPE = typename BIGGER_TYPE::type; SCRATCH_TYPE m_scratch{}; diff --git a/include/BufferWriter.h b/include/BufferWriter.h index 9e396c7..3befdb5 100644 --- a/include/BufferWriter.h +++ b/include/BufferWriter.h @@ -6,7 +6,9 @@ #define PROJECT_TEMPLATE_BUFFER_WRITER_H #include "Common.h" +#include #include +#include struct MeasureSize { @@ -14,28 +16,29 @@ struct MeasureSize { void writeBytes(const T& ) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bytesCount += SIZE; + _bitsCount += BITS_SIZE; } - template - void writeBits(const T& ) { + template + void writeBits(const T& , size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - static_assert(SIZE > 0 && SIZE <= BITS_SIZE, ""); - _bytesCount += SIZE * 8; + assert(bitsCount <= BITS_SIZE); + _bitsCount += bitsCount; } template void writeBuffer(const T* , size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bytesCount += SIZE * count; + _bitsCount += BITS_SIZE * count; } + //get size in bytes size_t getSize() const { - return _bytesCount; + return _bitsCount / 8; } private: - size_t _bytesCount{}; + size_t _bitsCount{}; }; @@ -43,7 +46,7 @@ private: struct BufferWriter { using value_type = uint8_t; BufferWriter(std::vector& buffer):_buf{buffer}, _outIt{std::back_inserter(buffer)} { - + static_assert(std::is_unsigned::value, ""); } template @@ -51,11 +54,11 @@ struct BufferWriter { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (m_scratchBits) { - using UT = typename std::make_unsigned::type; - writeBits(reinterpret_cast(v)); - } else { + if (!m_scratchBits) { directWrite(&v,1); + } else { + using UT = typename std::make_unsigned::type; + writeBits(reinterpret_cast(v), BITS_SIZE); } } @@ -63,41 +66,28 @@ struct BufferWriter { void writeBuffer(const T* buf, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (m_scratchBits) { - //todo implement -// using UT = typename std::make_unsigned::type; -// writeBits(reinterpret_cast(v)); - } else { + if (!m_scratchBits) { directWrite(buf, count); + } else { + using UT = typename std::make_unsigned::type; + //todo improve implementation + const auto end = buf + count; + for (auto it = buf; it != end; ++it) + writeBits(reinterpret_cast(*it), BITS_SIZE); } - } - template - void writeBits(const T& v) { + template + void writeBits(const T& v, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - static_assert(SIZE > 0 && SIZE <= BITS_SIZE, ""); - writeBitsInternal(v, SIZE); + assert(bitsCount <= BITS_SIZE); + assert( v <= (( 1ULL << bitsCount ) - 1 ) ); + writeBitsInternal(v, bitsCount); } - template - void writeBitsInternal(const T& v, size_t size) { - auto value = v; - auto bitsLeft = size; - while (bitsLeft > 0) { - auto bits = std::min(bitsLeft, BITS_SIZE); - m_scratch |= static_cast( value ) << m_scratchBits; - m_scratchBits += bits; - if ( m_scratchBits >= BITS_SIZE ) { - auto tmp = static_cast(m_scratch & bufTypeMask); - directWrite(&tmp, 1); - m_scratch >>= BITS_SIZE; - m_scratchBits -= BITS_SIZE; - - value >>= BITS_SIZE; - } - bitsLeft -= bits; - } + void align() { + if ( m_scratchBits ) + writeBitsInternal(value_type{}, BITS_SIZE - m_scratchBits); } void flush() { @@ -121,6 +111,26 @@ private: std::copy_n(reinterpret_cast(v), bytesSize, _buf.data()+pos); } + template + void writeBitsInternal(const T& v, size_t size) { + auto value = v; + auto bitsLeft = size; + while (bitsLeft > 0) { + auto bits = std::min(bitsLeft, BITS_SIZE); + m_scratch |= static_cast( value ) << m_scratchBits; + m_scratchBits += bits; + if ( m_scratchBits >= BITS_SIZE ) { + auto tmp = static_cast(m_scratch & bufTypeMask); + directWrite(&tmp, 1); + m_scratch >>= BITS_SIZE; + m_scratchBits -= BITS_SIZE; + + value >>= BITS_SIZE; + } + bitsLeft -= bits; + } + } + const value_type bufTypeMask = 0xFF; using SCRATCH_TYPE = typename BIGGER_TYPE::type; std::vector& _buf; diff --git a/include/Common.h b/include/Common.h index 7596e71..d4f91c4 100644 --- a/include/Common.h +++ b/include/Common.h @@ -48,14 +48,14 @@ struct BIGGER_TYPE { typedef int16_t type; }; -template -constexpr size_t DEFAULT_OR_SIZE =SIZE == 0 ? sizeof(T) : SIZE; +template +constexpr size_t ARITHMETIC_OR_ENUM_SIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(T) : 0; template struct ProcessAnyType { template static void serialize(S& s, T&& v) { - s.template value(v); + s.template value(std::forward(v)); } }; diff --git a/include/DeltaDeserializer.h b/include/DeltaDeserializer.h index bd6819a..d05d502 100644 --- a/include/DeltaDeserializer.h +++ b/include/DeltaDeserializer.h @@ -8,13 +8,14 @@ #include #include #include -#include "Common.h" +#include "Deserializer.h" template class DeltaDeserializer { public: - DeltaDeserializer(Reader& w, const TObj& oldObj, const TObj& newObj) - :_reader{w}, + DeltaDeserializer(Reader& r, const TObj& oldObj, const TObj& newObj) + :_deserializer{r}, + _reader{r}, _oldObj{oldObj}, _newObj{newObj}, _objMemPos(std::deque(1, ObjectMemoryPosition{oldObj, newObj})), @@ -40,7 +41,10 @@ public: template DeltaDeserializer& text(T&& str) { - return container(str, [this](auto& v) { value<1>(v);}); + if (getChangedState(str)) { + _deserializer.text(str); + } + return *this; } @@ -80,7 +84,7 @@ public: DeltaDeserializer& container(T& obj, Fnc&& fnc) { if(getChangedState(obj)) { size_t newSize{}; - _reader.template readBits<32>(newSize); + _reader.readBits(newSize, 32); if (!_isNewElement) { auto old = *_objMemPos.top().getOldObjectField(obj); if (old.size() != newSize) @@ -96,6 +100,7 @@ public: } private: + Deserializer _deserializer; Reader& _reader; const TObj& _oldObj; @@ -157,24 +162,26 @@ private: bool readChangedState() { unsigned char res{}; - _reader.template readBits<1>(res); + _reader.readBits(res, 1); return res; } + + size_t readIndexOffset() { //special case, if items are updated sequentialy unsigned char tmp{}; - _reader.template readBits<1>(tmp); + _reader.readBits(tmp, 1); if (tmp) { return 0u; } else { size_t res{}; - _reader.template readBits<1>(tmp); + _reader.readBits(tmp, 1); if (tmp > 0) - _reader.template readBits<4>(res); + _reader.readBits(res, 4); else - _reader.template readBits<32>(res); + _reader.readBits(res, 32); return res; } diff --git a/include/DeltaSerializer.h b/include/DeltaSerializer.h index b698115..f4dd74b 100644 --- a/include/DeltaSerializer.h +++ b/include/DeltaSerializer.h @@ -8,13 +8,14 @@ #include #include #include -#include "Common.h" +#include "Serializer.h" template class DeltaSerializer { public: DeltaSerializer(Writter& w, const TObj& oldObj, const TObj& newObj) - :_writter{w}, + :_serializer{w}, + _writter{w}, _oldObj{oldObj}, _newObj{newObj}, _objMemPos(std::deque(1, ObjectMemoryPosition{oldObj, newObj})), @@ -42,7 +43,11 @@ public: template DeltaSerializer& text(T&& str) { - return container(str, [this](auto& v) { value<1>(v);}); + if(setChangedState(str)) { + _serializer.text(str); + } + return *this; + } template @@ -79,7 +84,7 @@ public: template DeltaSerializer& container(T&& obj, Fnc&& fnc) { if(setChangedState(obj)) { - _writter.template writeBits<32>(obj.size()); + _writter.writeBits(obj.size(), 32); if (!_isNewElement) { auto old = *_objMemPos.top().getOldObjectField(obj); processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj), std::forward(fnc)); @@ -92,6 +97,7 @@ public: } private: + Serializer _serializer; Writter& _writter; const TObj& _oldObj; const TObj& _newObj; @@ -159,21 +165,21 @@ private: } void writeChangedState(bool state) { - _writter.template writeBits<1>(state ? 1u : 0u); + _writter.writeBits(state ? 1u : 0u, 1); } void writeIndexOffset(const size_t offset) { //special case, if items are updated sequentialy if (offset == 0) { - _writter.template writeBits<1>(1u); + _writter.writeBits(1u, 1); } else { - _writter.template writeBits<1>(0u); + _writter.writeBits(0u, 1); auto smallOffset = offset < 16; - _writter.template writeBits<1>(smallOffset ? 1u : 0u); + _writter.writeBits(smallOffset ? 1u : 0u, 1); if (smallOffset) - _writter.template writeBits<4>(offset); + _writter.writeBits(offset, 4); else - _writter.template writeBits<32>(offset); + _writter.writeBits(offset, 32); } } diff --git a/include/Deserializer.h b/include/Deserializer.h index bef8e10..8111d58 100644 --- a/include/Deserializer.h +++ b/include/Deserializer.h @@ -103,9 +103,7 @@ public: decltype(obj.size()) size{}; readLength(size); obj.resize(size); - using VType = typename T::value_type; - constexpr auto VSIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(VType) : 0; - procContainer(obj); + procContainer>(obj); return *this; } @@ -130,8 +128,7 @@ public: template Deserializer & array(std::array &arr) { - constexpr auto VSIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(T) : 0; - procContainer(arr); + procContainer>(arr); return *this; } @@ -145,19 +142,39 @@ public: return *this; } + template + Deserializer& array(T (&arr)[N]) { + procCArray(arr); + return *this; + } + + template + Deserializer& array(T (&arr)[N]) { + procCArray>(arr); + return *this; + } private: Reader& _reader; void readLength(size_t& size) { size = {}; - _reader.template readBits<32>(size); + _reader.readBits(size, 32); } template void procContainer(T&& obj) { + //todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector specialization) for (auto& v: obj) ProcessAnyType::serialize(*this, v); }; + template + void procCArray(T (&arr)[N]) { + //todo could be improved for arithmetic types + T* end = arr + N; + for (T* it = arr; it != end; ++it) + ProcessAnyType::serialize(*this, *it); + }; + }; diff --git a/include/Serializer.h b/include/Serializer.h index 230c4e4..436c01d 100644 --- a/include/Serializer.h +++ b/include/Serializer.h @@ -85,9 +85,7 @@ public: template Serializer& container(const T& obj) { writeLength(obj.size()); - using VType = typename T::value_type; - constexpr auto VSIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(VType) : 0; - procContainer(obj); + procContainer>(obj); return *this; } @@ -112,8 +110,7 @@ public: template Serializer & array(const std::array &arr) { - constexpr auto VSIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(T) : 0; - procContainer(arr); + procContainer>(arr); return *this; } @@ -127,26 +124,40 @@ public: return *this; } - template + template Serializer& array(const T (&arr)[N]) { -// const T* end = arr + N; -// for (const T* tmp = arr; tmp != end; ++tmp) -// fnc(*tmp); + procCArray(arr); return *this; } + template + Serializer& array(const T (&arr)[N]) { + procCArray>(arr); + return *this; + } + + private: Writter& _writter; void writeLength(const size_t size) { - _writter.template writeBits<32>(size); + _writter.writeBits(size, 32); } template void procContainer(T&& obj) { + //todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector specialization) for (auto& v: obj) ProcessAnyType::serialize(*this, v); }; + template + void procCArray(T (&arr)[N]) { + //todo could be improved for arithmetic types + const T* end = arr + N; + for (const T* it = arr; it != end; ++it) + ProcessAnyType::serialize(*this, *it); + }; + template void procText(const T* str, size_t size) { writeLength(size); diff --git a/tests/BufferBitsOpTests.cpp b/tests/BufferBitsOpTests.cpp index 8b9bca3..9ee4ca3 100644 --- a/tests/BufferBitsOpTests.cpp +++ b/tests/BufferBitsOpTests.cpp @@ -39,11 +39,11 @@ TEST(BufferBitsOperations, WriteAndReadBits) { std::vector buf; BufferWriter bw{buf}; - bw.writeBits(data.a); - bw.writeBits(data.b); - bw.writeBits(data.c); - bw.writeBits(data.d); - bw.writeBits(data.e); + bw.writeBits(data.a, aBITS); + bw.writeBits(data.b, bBITS); + bw.writeBits(data.c, cBITS); + bw.writeBits(data.d, dBITS); + bw.writeBits(data.e, eBITS); bw.flush(); auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ; EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(bytesCount)); @@ -51,11 +51,11 @@ TEST(BufferBitsOperations, WriteAndReadBits) { BufferReader br{buf}; IntegralUnsignedTypes res; - br.readBits(res.a); - br.readBits(res.b); - br.readBits(res.c); - br.readBits(res.d); - br.readBits(res.e); + br.readBits(res.a, aBITS); + br.readBits(res.b, bBITS); + br.readBits(res.c, cBITS); + br.readBits(res.d, dBITS); + br.readBits(res.e, eBITS); EXPECT_THAT(res.a, Eq(data.a)); EXPECT_THAT(res.b, Eq(data.b)); @@ -70,7 +70,7 @@ TEST(BufferBitsOperations, WhenFinishedFlushWriter) { std::vector buf; BufferWriter bw{buf}; - bw.writeBits<2>(0xFFu); + bw.writeBits(3u, 2); EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(0)); bw.flush(); EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1)); @@ -84,24 +84,62 @@ TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) { std::vector buf; BufferWriter bw{buf}; - bw.writeBits<2>(0xFFu); + bw.writeBits(7u,3); bw.flush(); EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1)); //read from buffer BufferReader br{buf}; unsigned tmp; - EXPECT_THAT(br.readBits<4>(tmp), Eq(true)); - EXPECT_THAT(br.readBits<2>(tmp), Eq(true)); - EXPECT_THAT(br.readBits<2>(tmp), Eq(true)); - EXPECT_THAT(br.readBits<2>(tmp), Eq(false)); + EXPECT_THAT(br.readBits(tmp,4), Eq(true)); + EXPECT_THAT(br.readBits(tmp,2), Eq(true)); + EXPECT_THAT(br.readBits(tmp,2), Eq(true)); + EXPECT_THAT(br.readBits(tmp,2), Eq(false)); //part of next byte BufferReader br1{buf}; - EXPECT_THAT(br1.readBits<2>(tmp), Eq(true)); - EXPECT_THAT(br1.readBits<7>(tmp), Eq(false)); + EXPECT_THAT(br1.readBits(tmp,2), Eq(true)); + EXPECT_THAT(br1.readBits(tmp,7), Eq(false)); //bigger than byte BufferReader br2{buf}; - EXPECT_THAT(br2.readBits<9>(tmp), Eq(false)); + EXPECT_THAT(br2.readBits(tmp,9), Eq(false)); +} + +TEST(BufferBitsOperations, WhenAlignedFlushHasNoEffect) { + //setup data + + //create and write to buffer + std::vector buf; + BufferWriter bw{buf}; + + bw.writeBits(3u, 2); + bw.align(); + EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1)); + bw.flush(); + EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1)); +} + +TEST(BufferBitsOperations, AlignMustWriteZerosBits) { + //setup data + + //create and write to buffer + std::vector buf; + BufferWriter bw{buf}; + + //write 2 bits and align + bw.writeBits(3u, 2); + bw.align(); + + unsigned char tmp; + BufferReader br1{buf}; + br1.readBits(tmp,2); + //read aligned bits + EXPECT_THAT(br1.readBits(tmp,6), Eq(true)); + EXPECT_THAT(tmp, Eq(0)); + + BufferReader br2{buf}; + //read 2 bits + br2.readBits(tmp,2); + EXPECT_THAT(br2.align(), Eq(true)); } diff --git a/tests/SerializationContainerTests.cpp b/tests/SerializationContainerTests.cpp index c3279dd..dd73fa2 100644 --- a/tests/SerializationContainerTests.cpp +++ b/tests/SerializationContainerTests.cpp @@ -2,6 +2,8 @@ // Created by fraillt on 17.2.7. // + +#include #include "SerializationTestUtils.h" #include #include diff --git a/tests/SerializationFixedSizeArrayTests.cpp b/tests/SerializationFixedSizeArrayTests.cpp index e1a7d1b..0970b8e 100644 --- a/tests/SerializationFixedSizeArrayTests.cpp +++ b/tests/SerializationFixedSizeArrayTests.cpp @@ -2,8 +2,9 @@ // Created by fraillt on 17.2.9. // +#include #include "SerializationTestUtils.h" -#include +#include using testing::ContainerEq; using testing::Eq; @@ -71,3 +72,68 @@ TEST(SerializeFSArrayStdArray, CustomFunctionThatSerializesAnEmptyByteEveryEleme EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * (MyStruct1::SIZE + sizeof(char)))); EXPECT_THAT(res, ContainerEq(src)); } + + +TEST(SerializeFSArrayCArray, ArithmeticValues) { + SerializationContext ctx; + int src[4]{5,9,15,-459}; + int res[4]{}; + + ctx.createSerializer().array(src); + ctx.createDeserializer().array(res); + + EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * sizeof(int))); + EXPECT_THAT(res, ContainerEq(src)); + +} + +TEST(SerializeFSArrayCArray, ArithmeticValuesSettingValueSizeExplicitly) { + SerializationContext ctx; + int src[4]{5,9,15,-459}; + int res[4]{}; + + ctx.createSerializer().array(src); + ctx.createDeserializer().array(res); + + EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * sizeof(int))); + EXPECT_THAT(res, ContainerEq(src)); + +} + +TEST(SerializeFSArrayCArray, CompositeTypes) { + SerializationContext ctx; + MyStruct1 src[]{ + MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, + MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; + MyStruct1 res[7]{}; + + ctx.createSerializer().array(src); + ctx.createDeserializer().array(res); + + EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * MyStruct1::SIZE)); + EXPECT_THAT(res, ContainerEq(src)); +} +// +// +TEST(SerializeFSArrayCArray, CustomFunctionThatSerializesAnEmptyByteEveryElement) { + SerializationContext ctx; + MyStruct1 src[]{ + MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, + MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; + MyStruct1 res[7]{}; + + + auto ser = ctx.createSerializer(); + ser.array(src, [&ser](auto& v) { + char tmp{}; + ser.object(v).value(tmp); + }); + auto des = ctx.createDeserializer(); + des.array(res, [&des](auto& v) { + char tmp{}; + des.object(v).value(tmp); + }); + + EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * (MyStruct1::SIZE + sizeof(char)))); + EXPECT_THAT(res, ContainerEq(src)); +} diff --git a/tests/SerializationTestUtils.h b/tests/SerializationTestUtils.h index 3faaa90..50cd12e 100644 --- a/tests/SerializationTestUtils.h +++ b/tests/SerializationTestUtils.h @@ -5,11 +5,11 @@ #ifndef BITSERY_SERIALIZERTESTS_H #define BITSERY_SERIALIZERTESTS_H -#include #include #include #include "BufferWriter.h" #include "Serializer.h" +#include struct MyStruct1 { MyStruct1(int v1, int v2):i1{v1}, i2{v2} {} @@ -19,7 +19,7 @@ struct MyStruct1 { bool operator == (const MyStruct1& rhs) const { return i1 == rhs.i1 && i2 == rhs.i2; } - static constexpr size_t SIZE = sizeof(i1) + sizeof(i2); + static constexpr size_t SIZE = sizeof(MyStruct1::i1) + sizeof(MyStruct1::i2); }; SERIALIZE(MyStruct1) { @@ -41,7 +41,7 @@ struct MyStruct2 { bool operator == (const MyStruct2& rhs) const { return e1 == rhs.e1 && s1 == rhs.s1; } - static constexpr size_t SIZE = MyStruct1::SIZE + sizeof(e1); + static constexpr size_t SIZE = MyStruct1::SIZE + sizeof(MyStruct2::e1); }; SERIALIZE(MyStruct2) { diff --git a/tests/SerializationTextTests.cpp b/tests/SerializationTextTests.cpp index 3a08f30..6f04694 100644 --- a/tests/SerializationTextTests.cpp +++ b/tests/SerializationTextTests.cpp @@ -2,6 +2,7 @@ // Created by fraillt on 17.2.7. // +#include #include "SerializationTestUtils.h" using namespace testing; @@ -31,7 +32,7 @@ TEST(SerializerText, WhenSizeOfTypeNotEqualsOneThenSetSizeExplicitly) { EXPECT_THAT(res, ContainerEq(t1)); } -TEST(SerializerText, BasicStringAsBufferIsThreadedAsNullTerminatedString) { +TEST(SerializerText, BasicStringUseSizeMethodNotNullterminatedLength) { SerializationContext ctx; std::wstring t1(L"some random text\0xxxxxx", 20); std::wstring wres; @@ -44,21 +45,21 @@ TEST(SerializerText, BasicStringAsBufferIsThreadedAsNullTerminatedString) { EXPECT_THAT(wres.size(), Eq(t1.size())); EXPECT_THAT(wres.size(), Gt(std::char_traits::length(t1.data()))); -// SerializationContext ctx2; -// std::string t2("\0no one cares what is there", 10); -// std::string res; -// ctx2.createSerializer().text(t2); -// ctx2.createDeserializer().text(res); -// -// EXPECT_THAT(res, StrNe(t2)); -// EXPECT_THAT(res.size(), Eq(0)); -// -// SerializationContext ctx3; -// std::string t3("never ending buffer that doesnt fit in this string", 10); -// ctx3.createSerializer().text(t3); -// ctx3.createDeserializer().text(res); -// EXPECT_THAT(res, StrEq(t3)); -// EXPECT_THAT(res.size(), Eq(10)); + SerializationContext ctx2; + std::string t2("\0no one cares what is there", 10); + std::string res; + ctx2.createSerializer().text(t2); + ctx2.createDeserializer().text(res); + + EXPECT_THAT(res, StrEq(t2)); + EXPECT_THAT(res.size(), Eq(t2.size())); + + SerializationContext ctx3; + std::string t3("never ending buffer that doesnt fit in this string", 10); + ctx3.createSerializer().text(t3); + ctx3.createDeserializer().text(res); + EXPECT_THAT(res, StrEq(t3)); + EXPECT_THAT(res.size(), Eq(10)); } const int CARR_LENGTH = 10; diff --git a/tests/SerializerObjectsTests.cpp b/tests/SerializerObjectsTests.cpp index 07a0926..3e0327f 100644 --- a/tests/SerializerObjectsTests.cpp +++ b/tests/SerializerObjectsTests.cpp @@ -137,7 +137,7 @@ TEST(DeltaSerializer, GeneralConceptTest) { yNew.arr[2] = 0xFFFFFFFF; yNew.carr[1] = 0xFFFFFFFF; yNew.s = "labas dienaABC"; - yNew.vx[0].s = "very nigga"; + yNew.vx[0].s = "very opapa"; yNew.vx[1].s = "bla"; yNew.vx.push_back(X{ 3 });