mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-13 10:49:24 +00:00
initial concept commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea/
|
||||
build/
|
||||
cmake-build-debug/
|
||||
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(serialization)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
#add tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
4
README.md
Normal file
4
README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Bitsery #
|
||||
Header only C++ byte and bit serialization library.
|
||||
|
||||
Currently it is not usable
|
||||
57
ext/gtest/CMakeLists.txt
Normal file
57
ext/gtest/CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(gtest_builder C CXX)
|
||||
include(ExternalProject)
|
||||
|
||||
set(GTEST_FORCE_SHARED_CRT ON)
|
||||
set(GTEST_DISABLE_PTHREADS OFF)
|
||||
|
||||
if(MINGW)
|
||||
set(GTEST_DISABLE_PTHREADS ON)
|
||||
endif()
|
||||
|
||||
if (${USE_GMOCK})
|
||||
message("use gmock")
|
||||
set(BUILD_ARGS -DBUILD_GTEST=OFF -DBUILD_GMOCK=ON)
|
||||
else ()
|
||||
message("use gtest only")
|
||||
set(BUILD_ARGS -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF)
|
||||
endif()
|
||||
|
||||
if (WIN32 AND NOT MINGW)
|
||||
set(BUILD_ARGS ${BUILD_ARGS}
|
||||
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
|
||||
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs)
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
CMAKE_ARGS ${BUILD_ARGS}
|
||||
-Dgtest_force_shared_crt=${GTEST_FORCE_SHARED_CRT}
|
||||
-Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS}
|
||||
PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
# disable update command
|
||||
UPDATE_COMMAND ""
|
||||
# disable install step
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
#export variables
|
||||
ExternalProject_Get_Property(googletest source_dir)
|
||||
ExternalProject_Get_Property(googletest binary_dir)
|
||||
if (${USE_GMOCK})
|
||||
# need to include both googletest and googlemock
|
||||
set(GTEST_INCLUDE_DIRS ${source_dir}/googlemock/include ${source_dir}/googletest/include PARENT_SCOPE)
|
||||
set(GTEST_LIBS_DIR ${binary_dir}/googlemock PARENT_SCOPE)
|
||||
set(GTEST_LIBNAME gmock PARENT_SCOPE)
|
||||
set(GTEST_MAIN_LIBNAME gmock_main PARENT_SCOPE)
|
||||
set(GTEST_LINK_LIBNAMES gmock_main PARENT_SCOPE)
|
||||
else()
|
||||
set(GTEST_INCLUDE_DIRS ${source_dir}/googletest/include PARENT_SCOPE)
|
||||
set(GTEST_LIBS_DIR ${binary_dir}/googletest PARENT_SCOPE)
|
||||
set(GTEST_LIBNAME gtest PARENT_SCOPE)
|
||||
set(GTEST_MAIN_LIBNAME gtest_main PARENT_SCOPE)
|
||||
# need to include both libs gtest and gtest_main
|
||||
set(GTEST_LINK_LIBNAMES gtest gtest_main PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
|
||||
111
include/BufferReader.h
Normal file
111
include/BufferReader.h
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 2016-11-11.
|
||||
//
|
||||
|
||||
#ifndef PROJECT_TEMPLATE_BUFFER_READER_H
|
||||
#define PROJECT_TEMPLATE_BUFFER_READER_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
struct BufferReader {
|
||||
|
||||
using value_type = uint8_t;
|
||||
BufferReader(const std::vector<uint8_t>& buf):_buf{buf}, _pos{std::begin(buf)}{
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBytes(T& v) {
|
||||
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);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBuffer(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 {
|
||||
return directRead(buf, count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBits(T& v) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
|
||||
|
||||
const auto bytesRequired = SIZE > m_scratchBits
|
||||
? ((SIZE - 1 - m_scratchBits) >> 3) + 1u
|
||||
: 0u;
|
||||
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesRequired )
|
||||
return false;
|
||||
ReadBitsInternal(v, SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadBitsInternal(T& v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, BITS_SIZE<value_type>);
|
||||
if ( m_scratchBits < bits ) {
|
||||
value_type tmp;
|
||||
|
||||
std::copy_n(_pos, 1, reinterpret_cast<value_type *>(&tmp));
|
||||
std::advance(_pos, 1);
|
||||
|
||||
m_scratch |= static_cast<SCRATCH_TYPE>(tmp) << m_scratchBits;
|
||||
m_scratchBits += BITS_SIZE<value_type>;
|
||||
}
|
||||
auto shiftedRes = static_cast<T>(m_scratch & ( (static_cast<SCRATCH_TYPE>(1)<<bits) - 1 )) << (size - bitsLeft);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
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{};
|
||||
size_t m_scratchBits{}; ///< Number of bits currently in the scratch buffer. If the user wants to read more bits than this, we have to go fetch another dword from memory.
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //PROJECT_TEMPLATE_BUFFER_READER_H
|
||||
137
include/BufferWriter.h
Normal file
137
include/BufferWriter.h
Normal file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 2016-11-11.
|
||||
//
|
||||
|
||||
#ifndef PROJECT_TEMPLATE_BUFFER_WRITER_H
|
||||
#define PROJECT_TEMPLATE_BUFFER_WRITER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <algorithm>
|
||||
|
||||
struct MeasureSize {
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBytes(const T& ) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bytesCount += SIZE;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBits(const T& ) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
|
||||
_bytesCount += SIZE * 8;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t getSize() const {
|
||||
return _bytesCount;
|
||||
}
|
||||
private:
|
||||
size_t _bytesCount{};
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct BufferWriter {
|
||||
using value_type = uint8_t;
|
||||
BufferWriter(std::vector<uint8_t>& buffer):_buf{buffer}, _outIt{std::back_inserter(buffer)} {
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBytes(const T& v) {
|
||||
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 {
|
||||
directWrite(&v,1);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
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 {
|
||||
directWrite(buf, count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBits(const T& v) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
|
||||
WriteBitsInternal(v, SIZE);
|
||||
}
|
||||
|
||||
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 Flush() {
|
||||
if ( m_scratchBits )
|
||||
{
|
||||
auto tmp = static_cast<value_type>( m_scratch & bufTypeMask );
|
||||
directWrite(&tmp, 1);
|
||||
m_scratch >>= m_scratchBits;
|
||||
m_scratchBits -= m_scratchBits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
void directWrite(const T* v, size_t count) {
|
||||
const auto bytesSize = sizeof(T) * count;
|
||||
const auto pos = _buf.size();
|
||||
_buf.resize(pos + bytesSize);
|
||||
std::copy_n(reinterpret_cast<const value_type *>(v), bytesSize, _buf.data()+pos);
|
||||
}
|
||||
|
||||
const value_type bufTypeMask = 0xFF;
|
||||
using SCRATCH_TYPE = typename BIGGER_TYPE<value_type>::type;
|
||||
std::vector<value_type>& _buf;
|
||||
std::back_insert_iterator<std::vector<value_type>> _outIt;
|
||||
SCRATCH_TYPE m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
|
||||
//size_t _bufSize{};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //PROJECT_TEMPLATE_BUFFER_WRITER_H
|
||||
106
include/Common.h
Normal file
106
include/Common.h
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Created by fraillt on 17.1.5.
|
||||
//
|
||||
|
||||
#ifndef TMP_COMMON_H
|
||||
#define TMP_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t BITS_SIZE = sizeof(T) << 3;
|
||||
|
||||
template <typename T>
|
||||
struct BIGGER_TYPE {};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<uint8_t> {
|
||||
typedef uint16_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<uint16_t> {
|
||||
typedef uint32_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<uint32_t> {
|
||||
typedef uint64_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<int8_t> {
|
||||
typedef int16_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<int16_t> {
|
||||
typedef int32_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct BIGGER_TYPE<int32_t> {
|
||||
typedef int64_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
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 <size_t SIZE>
|
||||
struct ProcessAnyType {
|
||||
template <typename S, typename T>
|
||||
static void serialize(S& s, T&& v) {
|
||||
s.value<SIZE>(std::forward<T>(v));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ProcessAnyType<0> {
|
||||
template <typename S, typename T>
|
||||
static void serialize(S& s, T&& v) {
|
||||
s.object(std::forward<T>(v));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define SERIALIZE(ObjectType) \
|
||||
template <typename S, typename T, typename std::enable_if<std::is_same<T, ObjectType>::value || std::is_same<T, const ObjectType>::value>::type* = nullptr> \
|
||||
S& serialize(S& s, T& o)
|
||||
|
||||
|
||||
class ObjectMemoryPosition {
|
||||
public:
|
||||
|
||||
template <typename T>
|
||||
ObjectMemoryPosition(const T& oldObj, const T& newObj)
|
||||
:ObjectMemoryPosition{reinterpret_cast<const char*>(&oldObj), reinterpret_cast<const char*>(&newObj), sizeof(T)}
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isFieldsEquals(const T& newObjField) {
|
||||
return *getOldObjectField(newObjField) == newObjField;
|
||||
}
|
||||
template <typename T>
|
||||
const T* getOldObjectField(const T& field) {
|
||||
auto offset = reinterpret_cast<const char*>(&field) - newObj;
|
||||
return reinterpret_cast<const T*>(oldObj + offset);
|
||||
}
|
||||
private:
|
||||
|
||||
ObjectMemoryPosition(const char* objOld, const char* objNew, size_t )
|
||||
:oldObj{objOld},
|
||||
newObj{objNew}
|
||||
{
|
||||
}
|
||||
|
||||
const char* oldObj;
|
||||
const char* newObj;
|
||||
};
|
||||
|
||||
#endif //TMP_COMMON_H
|
||||
185
include/DeltaDeserializer.h
Normal file
185
include/DeltaDeserializer.h
Normal file
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// Created by fraillt on 17.1.10.
|
||||
//
|
||||
|
||||
#ifndef TMP_DELTADESERIALIZER_H
|
||||
#define TMP_DELTADESERIALIZER_H
|
||||
|
||||
#include <array>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
#include "Common.h"
|
||||
|
||||
template<typename Reader, typename TObj>
|
||||
class DeltaDeserializer {
|
||||
public:
|
||||
DeltaDeserializer(Reader& w, const TObj& oldObj, const TObj& newObj)
|
||||
:_reader{w},
|
||||
_oldObj{oldObj},
|
||||
_newObj{newObj},
|
||||
_objMemPos(std::deque<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
|
||||
_isNewElement{false}
|
||||
{
|
||||
};
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
DeltaDeserializer& value(T& v) {
|
||||
if (getChangedState(v)) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_reader.template ReadBytes<ValueSize>(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeltaDeserializer& object(T&& obj) {
|
||||
if (getChangedState(obj))
|
||||
return serialize(*this, std::forward<T>(obj));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DeltaDeserializer& text(T&& str) {
|
||||
return withContainer(str, [this](auto& v) { value<1>(v);});
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaDeserializer & withArray(std::array<T,N> &arr, Fnc&& fnc) {
|
||||
if (getChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc);
|
||||
} else {
|
||||
for (auto& v:arr)
|
||||
fnc(v);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaDeserializer& withArray(T (&arr)[N], Fnc&& fnc) {
|
||||
if (getChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
T* tmp = arr;
|
||||
processContainer(old, old + N, tmp, tmp + N, fnc);
|
||||
} else {
|
||||
T* tmp = arr;
|
||||
for (auto i=0u; i < N; ++i, ++tmp)
|
||||
fnc(*tmp);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
DeltaDeserializer& withContainer(T& obj, Fnc&& fnc) {
|
||||
if(getChangedState(obj)) {
|
||||
size_t newSize{};
|
||||
_reader.template ReadBits<32>(newSize);
|
||||
if (!_isNewElement) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(obj);
|
||||
if (old.size() != newSize)
|
||||
obj.resize(newSize);
|
||||
processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
} else {
|
||||
obj.resize(newSize);
|
||||
for (auto& v:obj)
|
||||
fnc(v);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Reader& _reader;
|
||||
|
||||
const TObj& _oldObj;
|
||||
const TObj& _newObj;
|
||||
std::stack<ObjectMemoryPosition> _objMemPos;
|
||||
bool _isNewElement;
|
||||
|
||||
template <typename T>
|
||||
bool getChangedState(T& obj) {
|
||||
if (!_isNewElement) {
|
||||
if (!readChangedState()) {
|
||||
obj = *_objMemPos.top().getOldObjectField(obj);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
bool getChangedState(T (&arr)[N]) {
|
||||
if (!_isNewElement) {
|
||||
if (!readChangedState()) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
auto end = arr + N;
|
||||
auto pOld = old;
|
||||
for (auto p=arr; p != end; ++p, ++pOld)
|
||||
*p = *pOld;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TConstIt, typename TIt, typename Fnc>
|
||||
bool processContainer(TConstIt oldBegin, TConstIt oldEnd, TIt begin, TIt end, Fnc&& fnc) {
|
||||
auto offset = readIndexOffset();
|
||||
auto p = begin;
|
||||
auto pOld = oldBegin;
|
||||
for (; p != end && pOld != oldEnd; ++p, ++pOld) {
|
||||
if (offset) {
|
||||
*p = *pOld;
|
||||
--offset;
|
||||
} else {
|
||||
_objMemPos.emplace(ObjectMemoryPosition{*pOld, *p});
|
||||
fnc(*p);
|
||||
_objMemPos.pop();
|
||||
offset = readIndexOffset();
|
||||
}
|
||||
}
|
||||
if (offset != 0 && pOld != oldEnd)
|
||||
return false;
|
||||
_isNewElement = true;
|
||||
for (; p != end; ++p, --offset)
|
||||
fnc(*p);
|
||||
_isNewElement = false;
|
||||
return offset == 0;
|
||||
|
||||
}
|
||||
|
||||
bool readChangedState() {
|
||||
unsigned char res{};
|
||||
_reader.template ReadBits<1>(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t readIndexOffset() {
|
||||
//special case, if items are updated sequentialy
|
||||
unsigned char tmp{};
|
||||
_reader.template ReadBits<1>(tmp);
|
||||
if (tmp) {
|
||||
return 0u;
|
||||
}
|
||||
else {
|
||||
size_t res{};
|
||||
_reader.template ReadBits<1>(tmp);
|
||||
if (tmp > 0)
|
||||
_reader.template ReadBits<4>(res);
|
||||
else
|
||||
_reader.template ReadBits<32>(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //TMP_DELTADESERIALIZER_H
|
||||
182
include/DeltaSerializer.h
Normal file
182
include/DeltaSerializer.h
Normal file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// Created by fraillt on 17.1.10.
|
||||
//
|
||||
|
||||
#ifndef TMP_DELTASERIALIZER_H
|
||||
#define TMP_DELTASERIALIZER_H
|
||||
|
||||
#include <array>
|
||||
#include <stack>
|
||||
#include <algorithm>
|
||||
#include "Common.h"
|
||||
|
||||
template<typename Writter, typename TObj>
|
||||
class DeltaSerializer {
|
||||
public:
|
||||
DeltaSerializer(Writter& w, const TObj& oldObj, const TObj& newObj)
|
||||
:_writter{w},
|
||||
_oldObj{oldObj},
|
||||
_newObj{newObj},
|
||||
_objMemPos(std::deque<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
|
||||
_isNewElement{false}
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
DeltaSerializer& value(const T& v) {
|
||||
if (setChangedState(v)) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_writter.template WriteBytes<ValueSize>(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeltaSerializer& object(T&& obj) {
|
||||
if (setChangedState(obj)) {
|
||||
serialize(*this, std::forward<T>(obj));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DeltaSerializer& text(T&& str) {
|
||||
return withContainer(str, [this](auto& v) { value<1>(v);});
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaSerializer & withArray(const std::array<T,N> &arr, Fnc&& fnc) {
|
||||
if (setChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto& old = *_objMemPos.top().getOldObjectField(arr);
|
||||
processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc);
|
||||
} else {
|
||||
for (auto& v:arr)
|
||||
fnc(v);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaSerializer& withArray(const T (&arr)[N], Fnc&& fnc) {
|
||||
if (setChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
const T* tmp = arr;
|
||||
processContainer(old, old + N, tmp, tmp + N, fnc);
|
||||
} else {
|
||||
const T* tmp = arr;
|
||||
for (auto i=0u; i < N; ++i, ++tmp)
|
||||
fnc(*tmp);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
DeltaSerializer& withContainer(T&& obj, Fnc&& fnc) {
|
||||
if(setChangedState(obj)) {
|
||||
_writter.template WriteBits<32>(obj.size());
|
||||
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));
|
||||
} else {
|
||||
for (auto& v:obj)
|
||||
fnc(v);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Writter& _writter;
|
||||
const TObj& _oldObj;
|
||||
const TObj& _newObj;
|
||||
std::stack<ObjectMemoryPosition> _objMemPos;
|
||||
bool _isNewElement;
|
||||
|
||||
template <typename T>
|
||||
bool setChangedState(const T& obj) {
|
||||
if (!_isNewElement) {
|
||||
auto res = !_objMemPos.top().isFieldsEquals(obj);
|
||||
writeChangedState(res);
|
||||
return res;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
bool setChangedState(const T (&arr)[N]) {
|
||||
if (!_isNewElement) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
auto end = arr + N;
|
||||
bool changed{};
|
||||
for (auto p=arr, pOld=old; p != end; ++p, ++pOld) {
|
||||
if (!(*p == *pOld)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
writeChangedState(changed);
|
||||
return changed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
void processContainer(const T oldBegin, const T oldEnd, const T begin, const T end, Fnc&& fnc) {
|
||||
auto misMatch = std::mismatch(oldBegin, oldEnd, begin, end);
|
||||
auto lastChanged = begin;
|
||||
while (misMatch.first != oldEnd && misMatch.second != end) {
|
||||
writeIndexOffset(std::distance(lastChanged, misMatch.second));
|
||||
_objMemPos.emplace(ObjectMemoryPosition{*misMatch.first, *misMatch.second});
|
||||
fnc(*misMatch.second);
|
||||
_objMemPos.pop();
|
||||
++misMatch.first;
|
||||
++misMatch.second;
|
||||
lastChanged = misMatch.second;
|
||||
misMatch = std::mismatch(misMatch.first, oldEnd, misMatch.second, end);
|
||||
}
|
||||
auto p = misMatch.second;
|
||||
//write items left
|
||||
writeIndexOffset(std::distance(lastChanged, end));
|
||||
//write old elements
|
||||
for (auto pOld = misMatch.first; p != end && pOld != oldEnd; ++p, ++pOld) {
|
||||
_objMemPos.emplace(ObjectMemoryPosition{*pOld, *p});
|
||||
fnc(*p);
|
||||
_objMemPos.pop();
|
||||
}
|
||||
|
||||
//write new elements
|
||||
_isNewElement = true;
|
||||
for (; p != end; ++p)
|
||||
fnc(*p);
|
||||
_isNewElement = false;
|
||||
}
|
||||
|
||||
void writeChangedState(bool state) {
|
||||
_writter.template WriteBits<1>(state ? 1u : 0u);
|
||||
}
|
||||
|
||||
void writeIndexOffset(const size_t offset) {
|
||||
//special case, if items are updated sequentialy
|
||||
if (offset == 0) {
|
||||
_writter.template WriteBits<1>(1u);
|
||||
} else {
|
||||
_writter.template WriteBits<1>(0u);
|
||||
auto smallOffset = offset < 16;
|
||||
_writter.template WriteBits<1>(smallOffset ? 1u : 0u);
|
||||
if (smallOffset)
|
||||
_writter.template WriteBits<4>(offset);
|
||||
else
|
||||
_writter.template WriteBits<32>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //TMP_DELTASERIALIZER_H
|
||||
116
include/Deserializer.h
Normal file
116
include/Deserializer.h
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 17.1.9.
|
||||
//
|
||||
|
||||
#ifndef TMP_DESERIALIZER_H
|
||||
#define TMP_DESERIALIZER_H
|
||||
|
||||
#include <array>
|
||||
|
||||
template<typename Reader>
|
||||
class Deserializer {
|
||||
public:
|
||||
Deserializer(Reader& r):_reader{r} {};
|
||||
|
||||
template <typename T>
|
||||
Deserializer& object(T&& obj) {
|
||||
return serialize(*this, std::forward<T>(obj));
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
static_assert(std::numeric_limits<float>::is_iec559, "");
|
||||
static_assert(std::numeric_limits<double>::is_iec559, "");
|
||||
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
using CT = std::conditional_t<std::is_same<T,float>::value, uint32_t, uint64_t>;
|
||||
static_assert(sizeof(CT) == ValueSize, "");
|
||||
_reader.template ReadBytes<ValueSize>(reinterpret_cast<CT&>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
using UT = std::underlying_type_t<T>;
|
||||
_reader.template ReadBytes<ValueSize>(reinterpret_cast<UT&>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_reader.template ReadBytes<ValueSize>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE = 1, typename T>
|
||||
Deserializer& text(std::basic_string<T>& str) {
|
||||
size_t size;
|
||||
readLength(size);
|
||||
std::vector<T> buf(size);
|
||||
_reader.template ReadBuffer<SIZE>(buf.data(), size);
|
||||
str.assign(buf.data(), size);
|
||||
// str.resize(size);
|
||||
// if (size)
|
||||
// _reader.template ReadBuffer<SIZE>(str.data(), size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer & withArray(std::array<T,N> &arr, Fnc && fnc) {
|
||||
for (auto& v: arr)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer& withArray(T (&arr)[N], Fnc&& fnc) {
|
||||
T* tmp = arr;
|
||||
for (auto i = 0u; i < N; ++i, ++tmp)
|
||||
fnc(*tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
Deserializer& withContainer(T&& obj, Fnc&& fnc) {
|
||||
decltype(obj.size()) size{};
|
||||
readLength(size);
|
||||
obj.resize(size);
|
||||
for (auto& v:obj)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
Deserializer& withContainer(T&& obj) {
|
||||
decltype(obj.size()) size{};
|
||||
readLength(size);
|
||||
obj.resize(size);
|
||||
for (auto& v: obj)
|
||||
object(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE, typename T>
|
||||
Deserializer& withContainer(T&& obj) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
writeLength(obj.size());
|
||||
for (auto& v: obj)
|
||||
value<ValueSize>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
Reader& _reader;
|
||||
void readLength(size_t& size) {
|
||||
_reader.template ReadBits<32>(size);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //TMP_DESERIALIZER_H
|
||||
120
include/Serializer.h
Normal file
120
include/Serializer.h
Normal file
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 17.1.3.
|
||||
//
|
||||
|
||||
#ifndef TMP_SERIALIZER_H
|
||||
#define TMP_SERIALIZER_H
|
||||
|
||||
#include <array>
|
||||
|
||||
template<typename Writter>
|
||||
class Serializer {
|
||||
public:
|
||||
Serializer(Writter& w):_writter{w} {};
|
||||
|
||||
template <typename T>
|
||||
Serializer& object(T&& obj) {
|
||||
return serialize(*this, std::forward<T>(obj));
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
Serializer& value(const T& v) {
|
||||
static_assert(std::numeric_limits<float>::is_iec559, "");
|
||||
static_assert(std::numeric_limits<double>::is_iec559, "");
|
||||
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
using CT = std::conditional_t<std::is_same<T,float>::value, uint32_t, uint64_t>;
|
||||
_writter.template WriteBytes<ValueSize>(reinterpret_cast<const CT&>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
|
||||
Serializer& value(const T& v) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_writter.template WriteBytes<ValueSize>(reinterpret_cast<const std::underlying_type_t<T>&>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
Serializer& value(const T& v) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_writter.template WriteBytes<ValueSize>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE = 1, typename T>
|
||||
Serializer& text(const std::basic_string<T>& str) {
|
||||
procText<SIZE>(str.data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE=1, typename T, size_t N>
|
||||
Serializer& text(const T (&str)[N]) {
|
||||
procText<SIZE>(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer & withArray(const std::array<T,N> &arr, Fnc&& fnc) {
|
||||
for (auto& v: arr)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer& withArray(const T (&arr)[N], Fnc&& fnc) {
|
||||
const T* end = arr + N;
|
||||
for (const T* tmp = arr; tmp != end; ++tmp)
|
||||
fnc(*tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
Serializer& withContainer(T&& obj, Fnc&& fnc) {
|
||||
writeLength(obj.size());
|
||||
for (auto& v: obj)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Serializer& withContainer(T&& obj) {
|
||||
writeLength(obj.size());
|
||||
procContainer<0>(std::forward<T>(obj));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE=0, typename T, typename std::enable_if<std::is_arithmetic<T>::value && std::is_enum<T>::value>::type>
|
||||
Serializer& withContainer(T&& obj) {
|
||||
writeLength(obj.size());
|
||||
//procContainer<SIZE == 0 ? sizeof(T) : SIZE>(std::forward<T>(obj));
|
||||
procContainer<DEFAULT_OR_SIZE<SIZE,T>>(std::forward<T>(obj));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Writter& _writter;
|
||||
void writeLength(const size_t size) {
|
||||
_writter.template WriteBits<32>(size);
|
||||
}
|
||||
|
||||
template <size_t SIZE, typename T>
|
||||
void procContainer(T&& obj) {
|
||||
for (auto& v: obj)
|
||||
ProcessAnyType<SIZE>::serialize(*this, v);
|
||||
};
|
||||
|
||||
template <size_t SIZE, typename T>
|
||||
void procText(const T* str) {
|
||||
const auto size = std::char_traits<T>::length(str);
|
||||
writeLength(size);
|
||||
if (size)
|
||||
_writter.template WriteBuffer<SIZE>(str, size);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //TMP_SERIALIZER_H
|
||||
107
tests/BufferBitsOpTests.cpp
Normal file
107
tests/BufferBitsOpTests.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 2017-01-10.
|
||||
//
|
||||
#include <gmock/gmock.h>
|
||||
#include "BufferWriter.h"
|
||||
#include "BufferReader.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint8_t c;
|
||||
uint8_t d;
|
||||
uint64_t e;
|
||||
};
|
||||
|
||||
TEST(BufferBitsOperations, WriteAndReadBits) {
|
||||
//setup data
|
||||
IntegralUnsignedTypes data;
|
||||
data.a = 485454;//bits 19
|
||||
data.b = 45978;//bits 16
|
||||
data.c = 0;//bits 1
|
||||
data.d = 36;//bits 6
|
||||
data.e = 479845648946;//bits 39
|
||||
|
||||
constexpr size_t aBITS = 21;
|
||||
constexpr size_t bBITS = 16;
|
||||
constexpr size_t cBITS = 5;
|
||||
constexpr size_t dBITS = 7;
|
||||
constexpr size_t eBITS = 40;
|
||||
|
||||
//create and write to buffer
|
||||
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.Flush();
|
||||
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(bytesCount));
|
||||
//read from buffer
|
||||
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);
|
||||
|
||||
EXPECT_THAT(res.a, Eq(data.a));
|
||||
EXPECT_THAT(res.b, Eq(data.b));
|
||||
EXPECT_THAT(res.c, Eq(data.c));
|
||||
EXPECT_THAT(res.d, Eq(data.d));
|
||||
EXPECT_THAT(res.e, Eq(data.e));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsOperations, WhenFinishedFlushWriter) {
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBits<2>(0xFFu);
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(0));
|
||||
bw.Flush();
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
std::vector<uint8_t> buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBits<2>(0xFFu);
|
||||
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));
|
||||
|
||||
//part of next byte
|
||||
BufferReader br1{buf};
|
||||
EXPECT_THAT(br1.ReadBits<2>(tmp), Eq(true));
|
||||
EXPECT_THAT(br1.ReadBits<7>(tmp), Eq(false));
|
||||
|
||||
//bigger than byte
|
||||
BufferReader br2{buf};
|
||||
EXPECT_THAT(br2.ReadBits<9>(tmp), Eq(false));
|
||||
}
|
||||
129
tests/BufferBytesOpTests.cpp
Normal file
129
tests/BufferBytesOpTests.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Created by Mindaugas Vinkelis on 2016-11-11.
|
||||
//
|
||||
#include <gmock/gmock.h>
|
||||
#include "BufferWriter.h"
|
||||
#include "BufferReader.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
uint32_t b;
|
||||
int16_t c;
|
||||
uint8_t d;
|
||||
int8_t e;
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
|
||||
TEST(BufferBytesOperations, WriteAndReadBytes) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.a = -4894541654564;
|
||||
data.b = 94545646;
|
||||
data.c = -8778;
|
||||
data.d = 200;
|
||||
data.e = -98;
|
||||
data.f[0] = 43;
|
||||
data.f[1] = -45;
|
||||
|
||||
//create and write to buffer
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBytes<4>(data.b);
|
||||
bw.WriteBytes<1>(data.f[0]);
|
||||
bw.WriteBytes<2>(data.c);
|
||||
bw.WriteBytes<1>(data.d);
|
||||
bw.WriteBytes<8>(data.a);
|
||||
bw.WriteBytes<1>(data.e);
|
||||
bw.WriteBytes<1>(data.f[1]);
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(18));
|
||||
//read from buffer
|
||||
BufferReader br{buf};
|
||||
IntegralTypes res{};
|
||||
EXPECT_THAT(br.ReadBytes<4>(res.b), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.f[0]), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<2>(res.c), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<8>(res.a), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.e), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.f[1]), Eq(true));
|
||||
//assert results
|
||||
|
||||
EXPECT_THAT(data.a, Eq(res.a));
|
||||
EXPECT_THAT(data.b, Eq(res.b));
|
||||
EXPECT_THAT(data.c, Eq(res.c));
|
||||
EXPECT_THAT(data.d, Eq(res.d));
|
||||
EXPECT_THAT(data.e, Eq(res.e));
|
||||
EXPECT_THAT(data.f, ContainerEq(res.f));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBytesOperations, ReadReturnsFalseIfNotEnoughBufferSize) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBytes<1>(a);
|
||||
bw.WriteBytes<1>(a);
|
||||
bw.WriteBytes<1>(a);
|
||||
//read from buffer
|
||||
BufferReader br{buf};
|
||||
int16_t b;
|
||||
int32_t c;
|
||||
EXPECT_THAT(br.ReadBytes<4>(c), Eq(false));
|
||||
EXPECT_THAT(br.ReadBytes<2>(b), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<2>(b), Eq(false));
|
||||
EXPECT_THAT(br.ReadBytes<1>(a), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(a), Eq(false));
|
||||
EXPECT_THAT(br.ReadBytes<2>(b), Eq(false));
|
||||
EXPECT_THAT(br.ReadBytes<4>(c), Eq(false));
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST(BufferBytesOperations, ReadIsCompletedWhenAllBytesAreRead) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.b = 94545646;
|
||||
data.c = -8778;
|
||||
data.d = 200;
|
||||
|
||||
//create and write to buffer
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBytes<4>(data.b);
|
||||
bw.WriteBytes<2>(data.c);
|
||||
bw.WriteBytes<1>(data.d);
|
||||
//read from buffer
|
||||
BufferReader br{buf};
|
||||
IntegralTypes res;
|
||||
EXPECT_THAT(br.ReadBytes<4>(res.b), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<2>(res.c), Eq(true));
|
||||
EXPECT_THAT(br.isCompleted(), Eq(false));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br.isCompleted(), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.d), Eq(false));
|
||||
EXPECT_THAT(br.isCompleted(), Eq(true));
|
||||
|
||||
BufferReader br1{buf};
|
||||
EXPECT_THAT(br1.ReadBytes<4>(res.b), Eq(true));
|
||||
EXPECT_THAT(br1.ReadBytes<2>(res.c), Eq(true));
|
||||
EXPECT_THAT(br1.isCompleted(), Eq(false));
|
||||
EXPECT_THAT(br1.ReadBytes<2>(res.c), Eq(false));
|
||||
EXPECT_THAT(br1.isCompleted(), Eq(false));
|
||||
EXPECT_THAT(br1.ReadBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br1.isCompleted(), Eq(true));
|
||||
|
||||
|
||||
}
|
||||
50
tests/CMakeLists.txt
Normal file
50
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
set(PROJECT_TEST_NAME serialization_tests)
|
||||
project(${PROJECT_TEST_NAME} C CXX)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
#add googletest external project
|
||||
#USE_GMOCK enable gmock
|
||||
#exports variables GTEST_INCLUDE_DIRS, GTEST_LIBS_DIR, GTEST_LIBNAME, GTEST_MAIN_LIBNAME
|
||||
set(EXT_PROJECTS_DIR ${CMAKE_SOURCE_DIR}/ext)
|
||||
set(USE_GMOCK ON)
|
||||
add_subdirectory(${EXT_PROJECTS_DIR}/gtest ${CMAKE_BINARY_DIR}/gtest)
|
||||
|
||||
# set common include folder for module
|
||||
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
file(GLOB TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
add_executable(${PROJECT_TEST_NAME} ${TEST_SRC_FILES})
|
||||
add_dependencies(${PROJECT_TEST_NAME} googletest)
|
||||
|
||||
set_property(TARGET ${PROJECT_TEST_NAME} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${PROJECT_TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
if(NOT WIN32 OR MINGW)
|
||||
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
|
||||
target_link_libraries(${PROJECT_TEST_NAME} ${GTEST_LIBS_DIR}/lib${LIBNAME}.a )
|
||||
ENDFOREACH()
|
||||
else()
|
||||
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
|
||||
target_link_libraries(${PROJECT_TEST_NAME}
|
||||
debug ${GTEST_LIBS_DIR}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
|
||||
optimized ${GTEST_LIBS_DIR}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
ENDFOREACH()
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_TEST_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_test(NAME all_tests COMMAND $<TARGET_FILE:${PROJECT_TEST_NAME}>)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
160
tests/SerializerObjectsTests.cpp
Normal file
160
tests/SerializerObjectsTests.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Created by fraillt on 17.1.31.
|
||||
//
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <Deserializer.h>
|
||||
#include <BufferReader.h>
|
||||
#include "BufferWriter.h"
|
||||
#include "Serializer.h"
|
||||
|
||||
#include "DeltaSerializer.h"
|
||||
#include "DeltaDeserializer.h"
|
||||
|
||||
#include <list>
|
||||
#include <array>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::StrEq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
struct X {
|
||||
X() {};
|
||||
X(int v) :x{ v } {}
|
||||
std::string s{};
|
||||
int x{};
|
||||
bool operator ==(const X& r) const {
|
||||
return r.x == x && r.s == s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Y {
|
||||
int y{};
|
||||
int carr[3];
|
||||
std::array<int, 3> arr;
|
||||
std::vector<X> vx;
|
||||
std::string s;
|
||||
};
|
||||
struct Z { X x{}; Y y{}; };
|
||||
|
||||
|
||||
SERIALIZE(Z)
|
||||
{
|
||||
s.object(o.x);
|
||||
s.object(o.y);
|
||||
return s;
|
||||
}
|
||||
|
||||
SERIALIZE(X)
|
||||
{
|
||||
return s.value(o.x)
|
||||
.text(o.s);
|
||||
}
|
||||
|
||||
SERIALIZE(Y)
|
||||
{
|
||||
auto writeInt = [&s](auto& v) { s.template value<4>(v); };
|
||||
s.text(o.s);
|
||||
s.template value<4>(o.y);
|
||||
s.withArray(o.arr, writeInt);
|
||||
s.withArray(o.carr, writeInt);
|
||||
s.withContainer(o.vx, [&s](auto& v) { s.object(v); });
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
TEST(Serializer, GeneralConceptTest) {
|
||||
//std::string buf;
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{ buf };
|
||||
Serializer<BufferWriter> ser(bw);
|
||||
Y y{};
|
||||
y.y = 3423;
|
||||
y.arr[0] = 111;
|
||||
y.arr[1] = 222;
|
||||
y.arr[2] = 333;
|
||||
y.carr[0] = 123;
|
||||
y.carr[1] = 456;
|
||||
y.carr[2] = 789;
|
||||
y.vx.push_back(X(234));
|
||||
y.vx.push_back(X(6245));
|
||||
y.vx.push_back(X(613461));
|
||||
y.s = "labal diena";
|
||||
|
||||
Z z{};
|
||||
z.y = y;
|
||||
z.x = X{ 234 };
|
||||
|
||||
|
||||
serialize(ser, y);
|
||||
serialize(ser, z);
|
||||
|
||||
BufferReader br{ buf };
|
||||
Deserializer<BufferReader> des(br);
|
||||
|
||||
Y yres{};
|
||||
Z zres{};
|
||||
serialize(des, yres);
|
||||
serialize(des, zres);
|
||||
|
||||
EXPECT_THAT(yres.y, Eq(y.y));
|
||||
EXPECT_THAT(yres.vx, ContainerEq(y.vx));
|
||||
EXPECT_THAT(yres.arr, ContainerEq(y.arr));
|
||||
EXPECT_THAT(yres.carr, ContainerEq(y.carr));
|
||||
EXPECT_THAT(yres.s, StrEq(y.s));
|
||||
|
||||
EXPECT_THAT(zres.y.y, Eq(z.y.y));
|
||||
EXPECT_THAT(zres.y.vx, ContainerEq(z.y.vx));
|
||||
EXPECT_THAT(zres.y.arr, ContainerEq(z.y.arr));
|
||||
EXPECT_THAT(zres.y.carr, ContainerEq(z.y.carr));
|
||||
EXPECT_THAT(zres.y.s, StrEq(z.y.s));
|
||||
EXPECT_THAT(zres.x.s, StrEq(z.x.s));
|
||||
EXPECT_THAT(zres.x.x, Eq(z.x.x));
|
||||
|
||||
}
|
||||
|
||||
TEST(DeltaSerializer, GeneralConceptTest) {
|
||||
//std::string buf;
|
||||
Y y{};
|
||||
y.y = 3423;
|
||||
y.arr[0] = 111;
|
||||
y.arr[1] = 222;
|
||||
y.arr[2] = 333;
|
||||
y.carr[0] = 123;
|
||||
y.carr[1] = 456;
|
||||
y.carr[2] = 789;
|
||||
y.vx.push_back(X(234));
|
||||
y.vx.push_back(X(6245));
|
||||
y.vx.push_back(X(613461));
|
||||
y.s = "labal diena";
|
||||
y.vx[0].s = "very nice";
|
||||
y.vx[1].s = "very nice string, that is a little bit longer that previous";
|
||||
|
||||
Y yRead = y;
|
||||
Y yNew = y;
|
||||
yNew.y = 111111;
|
||||
yNew.arr[2] = 0xFFFFFFFF;
|
||||
yNew.carr[1] = 0xFFFFFFFF;
|
||||
yNew.s = "labas dienaABC";
|
||||
yNew.vx[0].s = "very nigga";
|
||||
yNew.vx[1].s = "bla";
|
||||
yNew.vx.push_back(X{ 3 });
|
||||
|
||||
std::vector<uint8_t> buf;
|
||||
BufferWriter bw{ buf };
|
||||
DeltaSerializer<BufferWriter, Y> ser(bw, y, yNew);
|
||||
serialize(ser, yNew);
|
||||
bw.Flush();
|
||||
|
||||
BufferReader br{ buf };
|
||||
DeltaDeserializer<BufferReader, Y> des(br, y, yRead);
|
||||
serialize(des, yRead);
|
||||
|
||||
EXPECT_THAT(yRead.y, Eq(yNew.y));
|
||||
EXPECT_THAT(yRead.vx, ContainerEq(yNew.vx));
|
||||
EXPECT_THAT(yRead.arr, ContainerEq(yNew.arr));
|
||||
EXPECT_THAT(yRead.carr, ContainerEq(yNew.carr));
|
||||
EXPECT_THAT(yRead.s, StrEq(yNew.s));
|
||||
}
|
||||
|
||||
55
tests/SerializerValuesTests.cpp
Normal file
55
tests/SerializerValuesTests.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Created by fraillt on 17.1.5.
|
||||
//
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <Deserializer.h>
|
||||
#include <BufferReader.h>
|
||||
#include "BufferWriter.h"
|
||||
#include "Serializer.h"
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
template <typename T>
|
||||
bool SerializeDeserializeValue(const T& v) {
|
||||
T res{};
|
||||
std::vector<uint8_t> buf{};
|
||||
|
||||
BufferWriter bw{buf};
|
||||
Serializer<BufferWriter> ser(bw);
|
||||
ser.value(v);
|
||||
bw.Flush();
|
||||
|
||||
BufferReader br{buf};
|
||||
Deserializer<BufferReader> des(br);
|
||||
des.value(res);
|
||||
return v == res;
|
||||
}
|
||||
|
||||
TEST(SerializerValues, IntegerTypes) {
|
||||
EXPECT_THAT(SerializeDeserializeValue(-449874), Eq(true));
|
||||
EXPECT_THAT(SerializeDeserializeValue(34u), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializerValues, EnumTypes) {
|
||||
enum E1{
|
||||
A1,B1,C1,D1
|
||||
};
|
||||
EXPECT_THAT(SerializeDeserializeValue(E1::C1), Eq(true));
|
||||
enum class E2 {
|
||||
A2,B2,C2,D2
|
||||
};
|
||||
EXPECT_THAT(SerializeDeserializeValue(E2::B2), Eq(true));
|
||||
enum class E3:short {
|
||||
A3, B3, C3=4568, D3
|
||||
};
|
||||
EXPECT_THAT(SerializeDeserializeValue(E3::C3), Eq(true));
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializerValues, FloatingPointTypes) {
|
||||
EXPECT_THAT(SerializeDeserializeValue(-484.465), Eq(true));
|
||||
EXPECT_THAT(SerializeDeserializeValue(0.00000015f), Eq(true));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user