basic functionality is tested and is can be used

This commit is contained in:
fraillt
2017-02-10 16:09:59 +02:00
parent 2d71a92287
commit 8607338bfb
15 changed files with 431 additions and 157 deletions

100
README.md
View File

@@ -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 <iostream>
#include <vector>
#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<float> 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<uint8_t> buffer;
//2) create buffer writer that is able to write bytes or bits to buffer
BufferWriter bw{buffer};
//3) create serializer
Serializer<BufferWriter> 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<BufferReader> 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

View File

@@ -18,6 +18,7 @@ struct MyStruct {
std::vector<float> 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<uint8_t> buffer;
//2) create buffer writer that is able to write bytes or bits to buffer
BufferWriter bw{buffer};
//3) create serializer
Serializer<BufferWriter> 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<BufferReader> des{br};
//call same function with different arguments

View File

@@ -7,6 +7,7 @@
#include "Common.h"
#include <cassert>
#include <algorithm>
struct BufferReader {
@@ -21,9 +22,9 @@ struct BufferReader {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
return m_scratch
? readBits<SIZE * 8>(reinterpret_cast<UT&>(v))
: directRead(&v, 1);
return !m_scratch
? directRead(&v, 1)
: readBits(reinterpret_cast<UT&>(v), BITS_SIZE<T>);
}
template<size_t SIZE, typename T>
@@ -31,28 +32,59 @@ struct BufferReader {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (m_scratchBits) {
//todo implement
// using UT = typename std::make_unsigned<T>::type;
// writeBits<SIZE * 8 * count>(reinterpret_cast<const UT&>(v));
} else {
if (!m_scratchBits) {
return directRead(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it) {
if (!readBits(reinterpret_cast<UT&>(*it), BITS_SIZE<T>))
return false;
}
}
return true;
}
template<size_t SIZE, typename T>
bool readBits(T& v) {
template<typename T>
bool readBits(T& v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
assert(bitsCount <= BITS_SIZE<T>);
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<size_t>(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<value_type> - m_scratchBits);
return tmp == 0;
}
return true;
}
bool isCompleted() const {
return _pos == std::end(_buf);
}
private:
const std::vector<value_type>& _buf;
decltype(std::begin(_buf)) _pos;
template <typename T>
bool directRead(T* v, size_t count) {
static_assert(!std::is_const<T>::value, "");
const auto bytesCount = sizeof(T) * count;
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesCount)
return false;
std::copy_n(_pos, bytesCount, reinterpret_cast<value_type *>(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<value_type>& _buf;
decltype(std::begin(_buf)) _pos;
template <typename T>
bool directRead(T* v, size_t count) {
static_assert(!std::is_const<T>::value, "");
const auto bytesCount = sizeof(T) * count;
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesCount)
return false;
std::copy_n(_pos, bytesCount, reinterpret_cast<value_type *>(v));
std::advance(_pos, bytesCount);
return true;
}
using SCRATCH_TYPE = typename BIGGER_TYPE<value_type>::type;
SCRATCH_TYPE m_scratch{};

View File

@@ -6,7 +6,9 @@
#define PROJECT_TEMPLATE_BUFFER_WRITER_H
#include "Common.h"
#include <cassert>
#include <algorithm>
#include <iterator>
struct MeasureSize {
@@ -14,28 +16,29 @@ struct MeasureSize {
void writeBytes(const T& ) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_bytesCount += SIZE;
_bitsCount += BITS_SIZE<T>;
}
template<size_t SIZE, typename T>
void writeBits(const T& ) {
template<typename T>
void writeBits(const T& , size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
_bytesCount += SIZE * 8;
assert(bitsCount <= BITS_SIZE<T>);
_bitsCount += bitsCount;
}
template<size_t SIZE, typename T>
void writeBuffer(const T* , size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_bytesCount += SIZE * count;
_bitsCount += BITS_SIZE<T> * 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<uint8_t>& buffer):_buf{buffer}, _outIt{std::back_inserter(buffer)} {
static_assert(std::is_unsigned<value_type>::value, "");
}
template<size_t SIZE, typename T>
@@ -51,11 +54,11 @@ struct BufferWriter {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (m_scratchBits) {
using UT = typename std::make_unsigned<T>::type;
writeBits<SIZE * 8>(reinterpret_cast<const UT&>(v));
} else {
if (!m_scratchBits) {
directWrite(&v,1);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBits(reinterpret_cast<const UT&>(v), BITS_SIZE<T>);
}
}
@@ -63,41 +66,28 @@ struct BufferWriter {
void writeBuffer(const T* buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (m_scratchBits) {
//todo implement
// using UT = typename std::make_unsigned<T>::type;
// writeBits<SIZE * 8 * count>(reinterpret_cast<const UT&>(v));
} else {
if (!m_scratchBits) {
directWrite(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBits(reinterpret_cast<const UT&>(*it), BITS_SIZE<T>);
}
}
template<size_t SIZE, typename T>
void writeBits(const T& v) {
template<typename T>
void writeBits(const T& v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
writeBitsInternal(v, SIZE);
assert(bitsCount <= BITS_SIZE<T>);
assert( v <= (( 1ULL << bitsCount ) - 1 ) );
writeBitsInternal(v, bitsCount);
}
template <typename T>
void writeBitsInternal(const T& v, size_t size) {
auto value = v;
auto bitsLeft = size;
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, BITS_SIZE<value_type>);
m_scratch |= static_cast<SCRATCH_TYPE>( value ) << m_scratchBits;
m_scratchBits += bits;
if ( m_scratchBits >= BITS_SIZE<value_type> ) {
auto tmp = static_cast<value_type>(m_scratch & bufTypeMask);
directWrite(&tmp, 1);
m_scratch >>= BITS_SIZE<value_type>;
m_scratchBits -= BITS_SIZE<value_type>;
value >>= BITS_SIZE<value_type>;
}
bitsLeft -= bits;
}
void align() {
if ( m_scratchBits )
writeBitsInternal(value_type{}, BITS_SIZE<value_type> - m_scratchBits);
}
void flush() {
@@ -121,6 +111,26 @@ private:
std::copy_n(reinterpret_cast<const value_type *>(v), bytesSize, _buf.data()+pos);
}
template <typename T>
void writeBitsInternal(const T& v, size_t size) {
auto value = v;
auto bitsLeft = size;
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, BITS_SIZE<value_type>);
m_scratch |= static_cast<SCRATCH_TYPE>( value ) << m_scratchBits;
m_scratchBits += bits;
if ( m_scratchBits >= BITS_SIZE<value_type> ) {
auto tmp = static_cast<value_type>(m_scratch & bufTypeMask);
directWrite(&tmp, 1);
m_scratch >>= BITS_SIZE<value_type>;
m_scratchBits -= BITS_SIZE<value_type>;
value >>= BITS_SIZE<value_type>;
}
bitsLeft -= bits;
}
}
const value_type bufTypeMask = 0xFF;
using SCRATCH_TYPE = typename BIGGER_TYPE<value_type>::type;
std::vector<value_type>& _buf;

View File

@@ -48,14 +48,14 @@ struct BIGGER_TYPE<char> {
typedef int16_t type;
};
template <size_t SIZE, typename T>
constexpr size_t DEFAULT_OR_SIZE =SIZE == 0 ? sizeof(T) : SIZE;
template <typename T>
constexpr size_t ARITHMETIC_OR_ENUM_SIZE = std::is_arithmetic<T>::value || std::is_enum<T>::value ? sizeof(T) : 0;
template <size_t SIZE>
struct ProcessAnyType {
template <typename S, typename T>
static void serialize(S& s, T&& v) {
s.template value<SIZE>(v);
s.template value<SIZE>(std::forward<T>(v));
}
};

View File

@@ -8,13 +8,14 @@
#include <array>
#include <stack>
#include <algorithm>
#include "Common.h"
#include "Deserializer.h"
template<typename Reader, typename TObj>
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<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
@@ -40,7 +41,10 @@ public:
template<typename T>
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<Reader> _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;
}

View File

@@ -8,13 +8,14 @@
#include <array>
#include <stack>
#include <algorithm>
#include "Common.h"
#include "Serializer.h"
template<typename Writter, typename TObj>
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<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
@@ -42,7 +43,11 @@ public:
template<typename T>
DeltaSerializer& text(T&& str) {
return container(str, [this](auto& v) { value<1>(v);});
if(setChangedState(str)) {
_serializer.text(str);
}
return *this;
}
template<typename T, size_t N, typename Fnc>
@@ -79,7 +84,7 @@ public:
template <typename T, typename Fnc>
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>(fnc));
@@ -92,6 +97,7 @@ public:
}
private:
Serializer<Writter> _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);
}
}

View File

@@ -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<VType>::value || std::is_enum<VType>::value ? sizeof(VType) : 0;
procContainer<VSIZE>(obj);
procContainer<ARITHMETIC_OR_ENUM_SIZE<typename T::value_type>>(obj);
return *this;
}
@@ -130,8 +128,7 @@ public:
template<typename T, size_t N>
Deserializer & array(std::array<T,N> &arr) {
constexpr auto VSIZE = std::is_arithmetic<T>::value || std::is_enum<T>::value ? sizeof(T) : 0;
procContainer<VSIZE>(arr);
procContainer<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
@@ -145,19 +142,39 @@ public:
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Deserializer& array(T (&arr)[N]) {
procCArray<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Deserializer& array(T (&arr)[N]) {
procCArray<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
private:
Reader& _reader;
void readLength(size_t& size) {
size = {};
_reader.template readBits<32>(size);
_reader.readBits(size, 32);
}
template <size_t VSIZE, typename T>
void procContainer(T&& obj) {
//todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector<bool> specialization)
for (auto& v: obj)
ProcessAnyType<VSIZE>::serialize(*this, v);
};
template <size_t VSIZE, typename T, size_t N>
void procCArray(T (&arr)[N]) {
//todo could be improved for arithmetic types
T* end = arr + N;
for (T* it = arr; it != end; ++it)
ProcessAnyType<VSIZE>::serialize(*this, *it);
};
};

View File

@@ -85,9 +85,7 @@ public:
template <typename T>
Serializer& container(const T& obj) {
writeLength(obj.size());
using VType = typename T::value_type;
constexpr auto VSIZE = std::is_arithmetic<VType>::value || std::is_enum<VType>::value ? sizeof(VType) : 0;
procContainer<VSIZE>(obj);
procContainer<ARITHMETIC_OR_ENUM_SIZE<typename T::value_type>>(obj);
return *this;
}
@@ -112,8 +110,7 @@ public:
template<typename T, size_t N>
Serializer & array(const std::array<T,N> &arr) {
constexpr auto VSIZE = std::is_arithmetic<T>::value || std::is_enum<T>::value ? sizeof(T) : 0;
procContainer<VSIZE>(arr);
procContainer<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
@@ -127,26 +124,40 @@ public:
return *this;
}
template<typename T, size_t N>
template<size_t VSIZE, typename T, size_t N>
Serializer& array(const T (&arr)[N]) {
// const T* end = arr + N;
// for (const T* tmp = arr; tmp != end; ++tmp)
// fnc(*tmp);
procCArray<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Serializer& array(const T (&arr)[N]) {
procCArray<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
private:
Writter& _writter;
void writeLength(const size_t size) {
_writter.template writeBits<32>(size);
_writter.writeBits(size, 32);
}
template <size_t VSIZE, typename T>
void procContainer(T&& obj) {
//todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector<bool> specialization)
for (auto& v: obj)
ProcessAnyType<VSIZE>::serialize(*this, v);
};
template <size_t VSIZE, typename T, size_t N>
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<VSIZE>::serialize(*this, *it);
};
template <size_t VSIZE, typename T>
void procText(const T* str, size_t size) {
writeLength(size);

View File

@@ -39,11 +39,11 @@ TEST(BufferBitsOperations, WriteAndReadBits) {
std::vector<uint8_t> buf;
BufferWriter bw{buf};
bw.writeBits<aBITS>(data.a);
bw.writeBits<bBITS>(data.b);
bw.writeBits<cBITS>(data.c);
bw.writeBits<dBITS>(data.d);
bw.writeBits<eBITS>(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<aBITS>(res.a);
br.readBits<bBITS>(res.b);
br.readBits<cBITS>(res.c);
br.readBits<dBITS>(res.d);
br.readBits<eBITS>(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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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));
}

View File

@@ -2,6 +2,8 @@
// Created by fraillt on 17.2.7.
//
#include <gmock/gmock.h>
#include "SerializationTestUtils.h"
#include <numeric>
#include <deque>

View File

@@ -2,8 +2,9 @@
// Created by fraillt on 17.2.9.
//
#include <gmock/gmock.h>
#include "SerializationTestUtils.h"
#include <functional>
#include <type_traits>
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<decltype(src)>::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<sizeof(int)>(src);
ctx.createDeserializer().array<sizeof(int)>(res);
EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent<decltype(src)>::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<decltype(src)>::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<decltype(src)>::value * (MyStruct1::SIZE + sizeof(char))));
EXPECT_THAT(res, ContainerEq(src));
}

View File

@@ -5,11 +5,11 @@
#ifndef BITSERY_SERIALIZERTESTS_H
#define BITSERY_SERIALIZERTESTS_H
#include <gmock/gmock.h>
#include <Deserializer.h>
#include <BufferReader.h>
#include "BufferWriter.h"
#include "Serializer.h"
#include <memory>
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) {

View File

@@ -2,6 +2,7 @@
// Created by fraillt on 17.2.7.
//
#include <gmock/gmock.h>
#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<std::wstring::value_type>::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;

View File

@@ -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 });