mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 08:13:56 +00:00
written more tests and first code example
This commit is contained in:
@@ -1,11 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(serialization)
|
||||
set(PROJECT_NAME bitsery)
|
||||
set(TEST_PROJECT_NAME ${PROJECT_NAME}_tests)
|
||||
project(${PROJECT_NAME})
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -O0 -fprofile-arcs -ftest-coverage")
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/ext)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
include(CodeCoverage)
|
||||
setup_target_for_coverage(${PROJECT_NAME}_coverage ${TEST_PROJECT_NAME} coverage)
|
||||
endif()
|
||||
|
||||
add_subdirectory(examples)
|
||||
|
||||
#add tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Bitsery #
|
||||
Header only C++ byte and bit serialization library.
|
||||
Header only C++ binary serialization library.
|
||||
|
||||
Currently it is not usable
|
||||
|
||||
11
examples/CMakeLists.txt
Normal file
11
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
file(GLOB EXAMPLE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
|
||||
FOREACH(EXAMPLE ${EXAMPLE_FILES})
|
||||
get_filename_component(EXAMPLE_NAME ${EXAMPLE} NAME_WE)
|
||||
add_executable(${EXAMPLE_NAME} ${EXAMPLE})
|
||||
ENDFOREACH()
|
||||
|
||||
70
examples/basic_usage.cpp
Normal file
70
examples/basic_usage.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// Created by fraillt on 17.2.9.
|
||||
//
|
||||
#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;
|
||||
};
|
||||
|
||||
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
|
||||
std::vector<uint8_t> buffer;
|
||||
BufferWriter bw{buffer};
|
||||
Serializer<BufferWriter> ser{bw};
|
||||
//call serialize function
|
||||
serialize(ser, data);
|
||||
//this is required if using bit operations
|
||||
bw.flush();
|
||||
|
||||
MyStruct result{};
|
||||
//create deserializer
|
||||
BufferReader br{buffer};
|
||||
Deserializer<BufferReader> des{br};
|
||||
|
||||
//call same function with different arguments
|
||||
serialize(des, result);
|
||||
|
||||
//print results
|
||||
print("initial data", data);
|
||||
print("result", result);
|
||||
}
|
||||
197
ext/CodeCoverage.cmake
Normal file
197
ext/CodeCoverage.cmake
Normal file
@@ -0,0 +1,197 @@
|
||||
# Copyright (c) 2012 - 2015, Lars Bilke
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
#
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# USAGE:
|
||||
|
||||
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
|
||||
# http://stackoverflow.com/a/22404544/80480
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt:
|
||||
# INCLUDE(CodeCoverage)
|
||||
#
|
||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
||||
#
|
||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
||||
# which runs your test executable and produces a lcov code coverage report:
|
||||
# Example:
|
||||
# SETUP_TARGET_FOR_COVERAGE(
|
||||
# my_coverage_target # Name for custom target.
|
||||
# test_driver # Name of the test driver executable that runs the tests.
|
||||
# # NOTE! This should always have a ZERO as exit code
|
||||
# # otherwise the coverage generation will not complete.
|
||||
# coverage # Name of output directory.
|
||||
# )
|
||||
#
|
||||
# 4. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
#
|
||||
|
||||
# Check prereqs
|
||||
FIND_PROGRAM( GCOV_PATH gcov )
|
||||
FIND_PROGRAM( LCOV_PATH lcov )
|
||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
||||
|
||||
IF(NOT GCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
||||
ENDIF() # NOT GCOV_PATH
|
||||
|
||||
IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||
IF("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
|
||||
MESSAGE(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||
ENDIF()
|
||||
ELSEIF(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||
ENDIF() # CHECK VALID COMPILER
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_C_FLAGS_COVERAGE
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
MARK_AS_ADVANCED(
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
|
||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
||||
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests.
|
||||
# MUST return ZERO always, even on errors.
|
||||
# If not, no coverage report will be created!
|
||||
# Param _outputname lcov output is generated as _outputname.info
|
||||
# HTML report is generated in _outputname/index.html
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT LCOV_PATH)
|
||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
||||
ENDIF() # NOT LCOV_PATH
|
||||
|
||||
IF(NOT GENHTML_PATH)
|
||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
ENDIF() # NOT GENHTML_PATH
|
||||
|
||||
SET(coverage_info "${CMAKE_BINARY_DIR}/${_outputname}.info")
|
||||
SET(coverage_cleaned "${coverage_info}.cleaned")
|
||||
|
||||
SEPARATE_ARGUMENTS(test_command UNIX_COMMAND "${_testrunner}")
|
||||
|
||||
# Setup target
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Cleanup lcov
|
||||
${LCOV_PATH} --directory . --zerocounters
|
||||
|
||||
# Run tests
|
||||
COMMAND ${test_command} ${ARGV3}
|
||||
|
||||
# Capturing lcov counters and generating report
|
||||
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${coverage_info}
|
||||
COMMAND ${LCOV_PATH} --remove ${coverage_info} 'tests/*' '/usr/*' --output-file ${coverage_cleaned}
|
||||
COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned}
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
||||
|
||||
# Param _targetname The name of new the custom make target
|
||||
# Param _testrunner The name of the target which runs the tests
|
||||
# Param _outputname cobertura output is generated as _outputname.xml
|
||||
# Optional fourth parameter is passed as arguments to _testrunner
|
||||
# Pass them in list form, e.g.: "-j;2" for -j 2
|
||||
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
|
||||
|
||||
IF(NOT PYTHON_EXECUTABLE)
|
||||
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
|
||||
ENDIF() # NOT PYTHON_EXECUTABLE
|
||||
|
||||
IF(NOT GCOVR_PATH)
|
||||
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
ENDIF() # NOT GCOVR_PATH
|
||||
|
||||
ADD_CUSTOM_TARGET(${_targetname}
|
||||
|
||||
# Run tests
|
||||
${_testrunner} ${ARGV3}
|
||||
|
||||
# Running gcovr
|
||||
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
|
||||
)
|
||||
|
||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
|
||||
@@ -17,24 +17,24 @@ struct BufferReader {
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBytes(T& v) {
|
||||
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))
|
||||
? readBits<SIZE * 8>(reinterpret_cast<UT&>(v))
|
||||
: directRead(&v, 1);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBuffer(T* buf, size_t count) {
|
||||
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));
|
||||
// writeBits<SIZE * 8 * count>(reinterpret_cast<const UT&>(v));
|
||||
} else {
|
||||
return directRead(buf, count);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ struct BufferReader {
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
bool ReadBits(T& v) {
|
||||
bool readBits(T& v) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
static_assert(SIZE > 0 && SIZE <= BITS_SIZE<T>, "");
|
||||
|
||||
@@ -52,12 +52,12 @@ struct BufferReader {
|
||||
: 0u;
|
||||
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesRequired )
|
||||
return false;
|
||||
ReadBitsInternal(v, SIZE);
|
||||
readBitsInternal(v, SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadBitsInternal(T& v, size_t size) {
|
||||
void readBitsInternal(T& v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
|
||||
@@ -11,21 +11,21 @@
|
||||
struct MeasureSize {
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBytes(const 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& ) {
|
||||
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) {
|
||||
void writeBuffer(const T* , size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bytesCount += SIZE * count;
|
||||
@@ -47,26 +47,26 @@ struct BufferWriter {
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBytes(const T& v) {
|
||||
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));
|
||||
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) {
|
||||
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));
|
||||
// writeBits<SIZE * 8 * count>(reinterpret_cast<const UT&>(v));
|
||||
} else {
|
||||
directWrite(buf, count);
|
||||
}
|
||||
@@ -74,14 +74,14 @@ struct BufferWriter {
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void WriteBits(const T& v) {
|
||||
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);
|
||||
writeBitsInternal(v, SIZE);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteBitsInternal(const T& v, size_t size) {
|
||||
void writeBitsInternal(const T& v, size_t size) {
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
@@ -100,7 +100,7 @@ struct BufferWriter {
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
void flush() {
|
||||
if ( m_scratchBits )
|
||||
{
|
||||
auto tmp = static_cast<value_type>( m_scratch & bufTypeMask );
|
||||
|
||||
@@ -55,7 +55,7 @@ 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));
|
||||
s.template value<SIZE>(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
DeltaDeserializer& value(T& v) {
|
||||
if (getChangedState(v)) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_reader.template ReadBytes<ValueSize>(v);
|
||||
_reader.template readBytes<ValueSize>(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -40,13 +40,13 @@ public:
|
||||
|
||||
template<typename T>
|
||||
DeltaDeserializer& text(T&& str) {
|
||||
return withContainer(str, [this](auto& v) { value<1>(v);});
|
||||
return container(str, [this](auto& v) { value<1>(v);});
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaDeserializer & withArray(std::array<T,N> &arr, Fnc&& fnc) {
|
||||
DeltaDeserializer & array(std::array<T,N> &arr, Fnc&& fnc) {
|
||||
if (getChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaDeserializer& withArray(T (&arr)[N], Fnc&& fnc) {
|
||||
DeltaDeserializer& array(T (&arr)[N], Fnc&& fnc) {
|
||||
if (getChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
@@ -77,10 +77,10 @@ public:
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
DeltaDeserializer& withContainer(T& obj, Fnc&& fnc) {
|
||||
DeltaDeserializer& container(T& obj, Fnc&& fnc) {
|
||||
if(getChangedState(obj)) {
|
||||
size_t newSize{};
|
||||
_reader.template ReadBits<32>(newSize);
|
||||
_reader.template readBits<32>(newSize);
|
||||
if (!_isNewElement) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(obj);
|
||||
if (old.size() != newSize)
|
||||
@@ -157,24 +157,24 @@ private:
|
||||
|
||||
bool readChangedState() {
|
||||
unsigned char res{};
|
||||
_reader.template ReadBits<1>(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);
|
||||
_reader.template readBits<1>(tmp);
|
||||
if (tmp) {
|
||||
return 0u;
|
||||
}
|
||||
else {
|
||||
size_t res{};
|
||||
_reader.template ReadBits<1>(tmp);
|
||||
_reader.template readBits<1>(tmp);
|
||||
if (tmp > 0)
|
||||
_reader.template ReadBits<4>(res);
|
||||
_reader.template readBits<4>(res);
|
||||
else
|
||||
_reader.template ReadBits<32>(res);
|
||||
_reader.template readBits<32>(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
DeltaSerializer& value(const T& v) {
|
||||
if (setChangedState(v)) {
|
||||
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
|
||||
_writter.template WriteBytes<ValueSize>(v);
|
||||
_writter.template writeBytes<ValueSize>(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -42,11 +42,11 @@ public:
|
||||
|
||||
template<typename T>
|
||||
DeltaSerializer& text(T&& str) {
|
||||
return withContainer(str, [this](auto& v) { value<1>(v);});
|
||||
return container(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) {
|
||||
DeltaSerializer & array(const std::array<T,N> &arr, Fnc&& fnc) {
|
||||
if (setChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
const auto& old = *_objMemPos.top().getOldObjectField(arr);
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
DeltaSerializer& withArray(const T (&arr)[N], Fnc&& fnc) {
|
||||
DeltaSerializer& array(const T (&arr)[N], Fnc&& fnc) {
|
||||
if (setChangedState(arr)) {
|
||||
if (!_isNewElement) {
|
||||
auto old = *_objMemPos.top().getOldObjectField(arr);
|
||||
@@ -77,9 +77,9 @@ public:
|
||||
}
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
DeltaSerializer& withContainer(T&& obj, Fnc&& fnc) {
|
||||
DeltaSerializer& container(T&& obj, Fnc&& fnc) {
|
||||
if(setChangedState(obj)) {
|
||||
_writter.template WriteBits<32>(obj.size());
|
||||
_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));
|
||||
@@ -159,21 +159,21 @@ private:
|
||||
}
|
||||
|
||||
void writeChangedState(bool state) {
|
||||
_writter.template WriteBits<1>(state ? 1u : 0u);
|
||||
_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);
|
||||
_writter.template writeBits<1>(1u);
|
||||
} else {
|
||||
_writter.template WriteBits<1>(0u);
|
||||
_writter.template writeBits<1>(0u);
|
||||
auto smallOffset = offset < 16;
|
||||
_writter.template WriteBits<1>(smallOffset ? 1u : 0u);
|
||||
_writter.template writeBits<1>(smallOffset ? 1u : 0u);
|
||||
if (smallOffset)
|
||||
_writter.template WriteBits<4>(offset);
|
||||
_writter.template writeBits<4>(offset);
|
||||
else
|
||||
_writter.template WriteBits<32>(offset);
|
||||
_writter.template writeBits<32>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef TMP_DESERIALIZER_H
|
||||
#define TMP_DESERIALIZER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <array>
|
||||
|
||||
template<typename Reader>
|
||||
@@ -17,63 +18,69 @@ public:
|
||||
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>
|
||||
/*
|
||||
* value overloads
|
||||
*/
|
||||
|
||||
template<size_t VSIZE = 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;
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
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));
|
||||
_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>
|
||||
template<size_t VSIZE = 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;
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
using UT = std::underlying_type_t<T>;
|
||||
_reader.template ReadBytes<ValueSize>(reinterpret_cast<UT&>(v));
|
||||
_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>
|
||||
template<size_t VSIZE = 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);
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
_reader.template readBytes<ValueSize>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE = 1, typename T>
|
||||
/*
|
||||
* text overloads
|
||||
*/
|
||||
|
||||
template <size_t VSIZE = 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);
|
||||
_reader.template readBuffer<VSIZE>(buf.data(), size);
|
||||
str.assign(buf.data(), size);
|
||||
// str.resize(size);
|
||||
// if (size)
|
||||
// _reader.template ReadBuffer<SIZE>(str.data(), size);
|
||||
// _reader.template readBuffer<VSIZE>(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);
|
||||
template<size_t VSIZE=1, typename T, size_t N>
|
||||
Deserializer& text(T (&str)[N]) {
|
||||
size_t size;
|
||||
readLength(size);
|
||||
_reader.template readBuffer<VSIZE>(str, size);
|
||||
str[size] = {};
|
||||
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;
|
||||
}
|
||||
/*
|
||||
* container overloads
|
||||
*/
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
Deserializer& withContainer(T&& obj, Fnc&& fnc) {
|
||||
Deserializer& container(T&& obj, Fnc&& fnc) {
|
||||
decltype(obj.size()) size{};
|
||||
readLength(size);
|
||||
obj.resize(size);
|
||||
@@ -82,34 +89,75 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
Deserializer& withContainer(T&& obj) {
|
||||
template <size_t VSIZE, typename T>
|
||||
Deserializer& container(T& obj) {
|
||||
decltype(obj.size()) size{};
|
||||
readLength(size);
|
||||
obj.resize(size);
|
||||
for (auto& v: obj)
|
||||
object(v);
|
||||
procContainer<VSIZE>(obj);
|
||||
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);
|
||||
template <typename T>
|
||||
Deserializer& container(T& obj) {
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* array overloads (fixed size array (std::array, and c-style array))
|
||||
*/
|
||||
|
||||
//std::array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer & array(std::array<T,N> &arr, Fnc && fnc) {
|
||||
for (auto& v: arr)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Deserializer & array(std::array<T,N> &arr) {
|
||||
procContainer<VSIZE>(arr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer& array(T (&arr)[N], Fnc&& fnc) {
|
||||
T* tmp = arr;
|
||||
for (auto i = 0u; i < N; ++i, ++tmp)
|
||||
fnc(*tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Reader& _reader;
|
||||
void readLength(size_t& size) {
|
||||
_reader.template ReadBits<32>(size);
|
||||
size = {};
|
||||
_reader.template readBits<32>(size);
|
||||
}
|
||||
template <size_t VSIZE, typename T>
|
||||
void procContainer(T&& obj) {
|
||||
for (auto& v: obj)
|
||||
ProcessAnyType<VSIZE>::serialize(*this, v);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef TMP_SERIALIZER_H
|
||||
#define TMP_SERIALIZER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <array>
|
||||
|
||||
template<typename Writter>
|
||||
@@ -13,105 +14,144 @@ public:
|
||||
Serializer(Writter& w):_writter{w} {};
|
||||
|
||||
template <typename T>
|
||||
Serializer& object(T&& obj) {
|
||||
return serialize(*this, std::forward<T>(obj));
|
||||
Serializer& object(const T& obj) {
|
||||
return serialize(*this, obj);
|
||||
}
|
||||
|
||||
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
/*
|
||||
* value overloads
|
||||
*/
|
||||
|
||||
template<size_t VSIZE = 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;
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
using CT = std::conditional_t<std::is_same<T,float>::value, uint32_t, uint64_t>;
|
||||
_writter.template WriteBytes<ValueSize>(reinterpret_cast<const CT&>(v));
|
||||
_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>
|
||||
template<size_t VSIZE = 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));
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
_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>
|
||||
template<size_t VSIZE = 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);
|
||||
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
|
||||
_writter.template writeBytes<ValueSize>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t SIZE = 1, typename T>
|
||||
/*
|
||||
* text overloads
|
||||
*/
|
||||
|
||||
template <size_t VSIZE = 1, typename T>
|
||||
Serializer& text(const std::basic_string<T>& str) {
|
||||
procText<SIZE>(str.data());
|
||||
procText<VSIZE>(str.data(), str.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t SIZE=1, typename T, size_t N>
|
||||
template<size_t VSIZE=1, typename T, size_t N>
|
||||
Serializer& text(const T (&str)[N]) {
|
||||
procText<SIZE>(str);
|
||||
procText<VSIZE>(str, std::min(std::char_traits<T>::length(str), N-1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* container overloads
|
||||
*/
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
Serializer& container(const T& obj, Fnc&& fnc) {
|
||||
writeLength(obj.size());
|
||||
for (auto& v: obj)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t VSIZE, typename T>
|
||||
Serializer& container(const T& obj) {
|
||||
writeLength(obj.size());
|
||||
procContainer<VSIZE>(obj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* array overloads (fixed size array (std::array, and c-style array))
|
||||
*/
|
||||
|
||||
//std::array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer & withArray(const std::array<T,N> &arr, Fnc&& fnc) {
|
||||
Serializer & array(const std::array<T,N> &arr, Fnc&& fnc) {
|
||||
for (auto& v: arr)
|
||||
fnc(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Serializer & array(const std::array<T,N> &arr) {
|
||||
procContainer<VSIZE>(arr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer& withArray(const T (&arr)[N], Fnc&& fnc) {
|
||||
Serializer& array(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);
|
||||
template<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);
|
||||
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);
|
||||
_writter.template writeBits<32>(size);
|
||||
}
|
||||
|
||||
template <size_t SIZE, typename T>
|
||||
template <size_t VSIZE, typename T>
|
||||
void procContainer(T&& obj) {
|
||||
for (auto& v: obj)
|
||||
ProcessAnyType<SIZE>::serialize(*this, v);
|
||||
ProcessAnyType<VSIZE>::serialize(*this, v);
|
||||
};
|
||||
|
||||
template <size_t SIZE, typename T>
|
||||
void procText(const T* str) {
|
||||
const auto size = std::char_traits<T>::length(str);
|
||||
template <size_t VSIZE, typename T>
|
||||
void procText(const T* str, size_t size) {
|
||||
writeLength(size);
|
||||
if (size)
|
||||
_writter.template WriteBuffer<SIZE>(str, size);
|
||||
_writter.template writeBuffer<VSIZE>(str, size);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -39,23 +39,23 @@ 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.Flush();
|
||||
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);
|
||||
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));
|
||||
@@ -70,9 +70,9 @@ TEST(BufferBitsOperations, WhenFinishedFlushWriter) {
|
||||
std::vector<uint8_t> buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBits<2>(0xFFu);
|
||||
bw.writeBits<2>(0xFFu);
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(0));
|
||||
bw.Flush();
|
||||
bw.flush();
|
||||
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
|
||||
|
||||
}
|
||||
@@ -84,24 +84,24 @@ TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) {
|
||||
std::vector<uint8_t> buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBits<2>(0xFFu);
|
||||
bw.Flush();
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
EXPECT_THAT(br2.readBits<9>(tmp), Eq(false));
|
||||
}
|
||||
|
||||
@@ -36,24 +36,24 @@ TEST(BufferBytesOperations, WriteAndReadBytes) {
|
||||
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]);
|
||||
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));
|
||||
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));
|
||||
@@ -73,20 +73,20 @@ TEST(BufferBytesOperations, ReadReturnsFalseIfNotEnoughBufferSize) {
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBytes<1>(a);
|
||||
bw.WriteBytes<1>(a);
|
||||
bw.WriteBytes<1>(a);
|
||||
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));
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
@@ -102,27 +102,27 @@ TEST(BufferBytesOperations, ReadIsCompletedWhenAllBytesAreRead) {
|
||||
std::vector<uint8_t> buf{};
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.WriteBytes<4>(data.b);
|
||||
bw.WriteBytes<2>(data.c);
|
||||
bw.WriteBytes<1>(data.d);
|
||||
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.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.readBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br.isCompleted(), Eq(true));
|
||||
EXPECT_THAT(br.ReadBytes<1>(res.d), Eq(false));
|
||||
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.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.readBytes<2>(res.c), Eq(false));
|
||||
EXPECT_THAT(br1.isCompleted(), Eq(false));
|
||||
EXPECT_THAT(br1.ReadBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br1.readBytes<1>(res.d), Eq(true));
|
||||
EXPECT_THAT(br1.isCompleted(), Eq(true));
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
set(PROJECT_TEST_NAME serialization_tests)
|
||||
project(${PROJECT_TEST_NAME} C CXX)
|
||||
project(${TEST_PROJECT_NAME} C CXX)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -16,28 +15,28 @@ 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)
|
||||
add_executable(${TEST_PROJECT_NAME} ${TEST_SRC_FILES})
|
||||
add_dependencies(${TEST_PROJECT_NAME} googletest)
|
||||
|
||||
set_property(TARGET ${PROJECT_TEST_NAME} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${PROJECT_TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${TEST_PROJECT_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 )
|
||||
target_link_libraries(${TEST_PROJECT_NAME} ${GTEST_LIBS_DIR}/lib${LIBNAME}.a )
|
||||
ENDFOREACH()
|
||||
else()
|
||||
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
|
||||
target_link_libraries(${PROJECT_TEST_NAME}
|
||||
target_link_libraries(${TEST_PROJECT_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})
|
||||
target_link_libraries(${TEST_PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_test(NAME all_tests COMMAND $<TARGET_FILE:${PROJECT_TEST_NAME}>)
|
||||
add_test(NAME all_tests COMMAND $<TARGET_FILE:${TEST_PROJECT_NAME}>)
|
||||
|
||||
|
||||
|
||||
|
||||
147
tests/SerializationContainerTests.cpp
Normal file
147
tests/SerializationContainerTests.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Created by fraillt on 17.2.7.
|
||||
//
|
||||
|
||||
#include "SerializationTestUtils.h"
|
||||
#include <numeric>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
using testing::ContainerEq;
|
||||
using testing::Eq;
|
||||
|
||||
template <typename Container>
|
||||
Container getFilledContainer() {
|
||||
return {1,2,3,4,5,78,456,8,54};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class SerializeContainerArthmeticTypes:public testing::Test {
|
||||
public:
|
||||
using TContainer = T;
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
const TContainer src= getFilledContainer<TContainer>() ;
|
||||
TContainer res{};
|
||||
|
||||
size_t getExpectedBufSize(const SerializationContext& ctx) const {
|
||||
return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * sizeof(TValue);
|
||||
}
|
||||
};
|
||||
//std::forward_list is not supported, because it doesn't have size() method
|
||||
using SequenceContainersWithArthmeticTypes = ::testing::Types<
|
||||
std::vector<int>,
|
||||
std::list<float>,
|
||||
std::deque<unsigned short>>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeContainerArthmeticTypes, SequenceContainersWithArthmeticTypes);
|
||||
|
||||
TYPED_TEST(SerializeContainerArthmeticTypes, Values) {
|
||||
SerializationContext ctx{};
|
||||
|
||||
ctx.createSerializer().container(this->src);
|
||||
ctx.createDeserializer().container(this->res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx)));
|
||||
EXPECT_THAT(this->res, ContainerEq(this->src));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeContainerArthmeticTypes, ValuesWithExplicitSize) {
|
||||
SerializationContext ctx{};
|
||||
using TValue = typename TestFixture::TValue;
|
||||
|
||||
ctx.createSerializer().container<sizeof(TValue)>(this->src);
|
||||
ctx.createDeserializer().container<sizeof(TValue)>(this->res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx)));
|
||||
EXPECT_THAT(this->res, ContainerEq(this->src));
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeContainerArthmeticTypes, CustomFunctionIncrements) {
|
||||
SerializationContext ctx{};
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.container(this->src, [&ser](auto v ) {
|
||||
//increment by 1 before writing
|
||||
v++;
|
||||
ser.value(v);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
des.container(this->res, [&des](auto&v ) {
|
||||
des.value(v);
|
||||
//increment by 1 after reading
|
||||
v++;
|
||||
});
|
||||
//decrement result by 2, before comparing for eq
|
||||
for(auto& v:this->res)
|
||||
v -= 2;
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx)));
|
||||
EXPECT_THAT(this->res, ContainerEq(this->src));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
class SerializeContainerCompositeTypes:public testing::Test {
|
||||
public:
|
||||
using TContainer = T;
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
const TContainer src= getFilledContainer<TContainer>();
|
||||
TContainer res{};
|
||||
size_t getExpectedBufSize(const SerializationContext& ctx) const {
|
||||
return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * TValue::SIZE;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
std::vector<MyStruct1> getFilledContainer<std::vector<MyStruct1>>() {
|
||||
return {
|
||||
{0,1},
|
||||
{2,3},
|
||||
{4,5},
|
||||
{6,7},
|
||||
{8,9},
|
||||
{11,34},
|
||||
{5134,1532}
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
std::list<MyStruct2> getFilledContainer<std::list<MyStruct2>>() {
|
||||
return {
|
||||
{MyStruct2::V1, {0,1}} ,
|
||||
{MyStruct2::V3, {-45,45}}
|
||||
};
|
||||
}
|
||||
|
||||
using SequenceContainersWithCompositeTypes = ::testing::Types<
|
||||
std::vector<MyStruct1>,
|
||||
std::list<MyStruct2>>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeContainerCompositeTypes, SequenceContainersWithCompositeTypes);
|
||||
|
||||
TYPED_TEST(SerializeContainerCompositeTypes, DefaultSerializeFunction) {
|
||||
SerializationContext ctx{};
|
||||
|
||||
ctx.createSerializer().container(this->src);
|
||||
ctx.createDeserializer().container(this->res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx)));
|
||||
EXPECT_THAT(this->res, ContainerEq(this->src));
|
||||
}
|
||||
|
||||
|
||||
TYPED_TEST(SerializeContainerCompositeTypes, CustomFunctionThatDoNothing) {
|
||||
SerializationContext ctx{};
|
||||
|
||||
auto emptyFnc = [](auto v) {};
|
||||
ctx.createSerializer().container(this->src, emptyFnc);
|
||||
ctx.createDeserializer().container(this->res, emptyFnc);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(this->src.size())));
|
||||
}
|
||||
|
||||
|
||||
73
tests/SerializationFixedSizeArrayTests.cpp
Normal file
73
tests/SerializationFixedSizeArrayTests.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Created by fraillt on 17.2.9.
|
||||
//
|
||||
|
||||
#include "SerializationTestUtils.h"
|
||||
#include <functional>
|
||||
|
||||
using testing::ContainerEq;
|
||||
using testing::Eq;
|
||||
|
||||
TEST(SerializeFSArrayStdArray, ArithmeticValues) {
|
||||
SerializationContext ctx;
|
||||
std::array<int, 4> src{5,9,15,-459};
|
||||
std::array<int, 4> res{};
|
||||
|
||||
ctx.createSerializer().array(src);
|
||||
ctx.createDeserializer().array(res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * sizeof(int)));
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
|
||||
}
|
||||
|
||||
TEST(SerializeFSArrayStdArray, ArithmeticValuesSettingValueSizeExplicitly) {
|
||||
SerializationContext ctx;
|
||||
std::array<int, 4> src{5,9,15,-459};
|
||||
std::array<int, 4> res{};
|
||||
|
||||
ctx.createSerializer().array<sizeof(int)>(src);
|
||||
ctx.createDeserializer().array<sizeof(int)>(res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * sizeof(int)));
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
|
||||
}
|
||||
|
||||
TEST(SerializeFSArrayStdArray, CompositeTypes) {
|
||||
SerializationContext ctx;
|
||||
std::array<MyStruct1, 7> src{
|
||||
MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7},
|
||||
MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}};
|
||||
std::array<MyStruct1, 7> res{};
|
||||
|
||||
ctx.createSerializer().array(src);
|
||||
ctx.createDeserializer().array(res);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * MyStruct1::SIZE));
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
}
|
||||
//
|
||||
//
|
||||
TEST(SerializeFSArrayStdArray, CustomFunctionThatSerializesAnEmptyByteEveryElement) {
|
||||
SerializationContext ctx;
|
||||
std::array<MyStruct1, 7> src{
|
||||
MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7},
|
||||
MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}};
|
||||
std::array<MyStruct1, 7> res{};
|
||||
|
||||
|
||||
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(src.size() * (MyStruct1::SIZE + sizeof(char))));
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
}
|
||||
78
tests/SerializationTestUtils.h
Normal file
78
tests/SerializationTestUtils.h
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// Created by fraillt on 17.2.8.
|
||||
//
|
||||
|
||||
#ifndef BITSERY_SERIALIZERTESTS_H
|
||||
#define BITSERY_SERIALIZERTESTS_H
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <Deserializer.h>
|
||||
#include <BufferReader.h>
|
||||
#include "BufferWriter.h"
|
||||
#include "Serializer.h"
|
||||
|
||||
struct MyStruct1 {
|
||||
MyStruct1(int v1, int v2):i1{v1}, i2{v2} {}
|
||||
MyStruct1():MyStruct1{0,0} {}
|
||||
int i1;
|
||||
int i2;
|
||||
bool operator == (const MyStruct1& rhs) const {
|
||||
return i1 == rhs.i1 && i2 == rhs.i2;
|
||||
}
|
||||
static constexpr size_t SIZE = sizeof(i1) + sizeof(i2);
|
||||
};
|
||||
|
||||
SERIALIZE(MyStruct1) {
|
||||
return s.
|
||||
value(o.i1).
|
||||
value(o.i2);
|
||||
}
|
||||
|
||||
struct MyStruct2 {
|
||||
enum MyEnum {
|
||||
V1, V2, V3
|
||||
};
|
||||
|
||||
MyStruct2(MyEnum e, MyStruct1 s):e1{e}, s1{s} {}
|
||||
MyStruct2():MyStruct2{V1,{0,0}} {}
|
||||
|
||||
MyEnum e1;
|
||||
MyStruct1 s1;
|
||||
bool operator == (const MyStruct2& rhs) const {
|
||||
return e1 == rhs.e1 && s1 == rhs.s1;
|
||||
}
|
||||
static constexpr size_t SIZE = MyStruct1::SIZE + sizeof(e1);
|
||||
};
|
||||
|
||||
SERIALIZE(MyStruct2) {
|
||||
return s.
|
||||
value(o.e1).
|
||||
object(o.s1);
|
||||
}
|
||||
|
||||
class SerializationContext {
|
||||
std::vector<uint8_t> buf{};
|
||||
std::unique_ptr<BufferWriter> bw;
|
||||
std::unique_ptr<BufferReader> br;
|
||||
public:
|
||||
Serializer<BufferWriter> createSerializer() {
|
||||
bw = std::make_unique<BufferWriter>(buf);
|
||||
return {*bw};
|
||||
};
|
||||
|
||||
size_t getBufferSize() const {
|
||||
return buf.size();
|
||||
}
|
||||
//since all containers .size() method returns size_t, it cannot be dirrectly serialized, because size_t is platform dependant
|
||||
//this function returns number of bytes writen to buffer, when reading/writing size of container
|
||||
static size_t containerSizeSerializedBytesCount(size_t elemsCount) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
Deserializer<BufferReader> createDeserializer() {
|
||||
br = std::make_unique<BufferReader>(buf);
|
||||
return {*br};
|
||||
};
|
||||
};
|
||||
|
||||
#endif //BITSERY_SERIALIZERTESTS_H
|
||||
118
tests/SerializationTextTests.cpp
Normal file
118
tests/SerializationTextTests.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// Created by fraillt on 17.2.7.
|
||||
//
|
||||
|
||||
#include "SerializationTestUtils.h"
|
||||
using namespace testing;
|
||||
|
||||
TEST(SerializerText, BasicString) {
|
||||
SerializationContext ctx;
|
||||
std::string t1 = "some random text";
|
||||
std::string res;
|
||||
|
||||
ctx.createSerializer().text(t1);
|
||||
ctx.createDeserializer().text(res);
|
||||
|
||||
EXPECT_THAT(res, StrEq(t1));
|
||||
EXPECT_THAT(res, ContainerEq(t1));
|
||||
|
||||
}
|
||||
|
||||
TEST(SerializerText, WhenSizeOfTypeNotEqualsOneThenSetSizeExplicitly) {
|
||||
SerializationContext ctx;
|
||||
constexpr auto VSIZE = sizeof(char32_t);
|
||||
std::basic_string<char32_t> t1 = U"some random text";
|
||||
std::basic_string<char32_t> res;
|
||||
static_assert(VSIZE > 1, "on this system, all character types has sizeof == 1, cannot run this tests");
|
||||
|
||||
ctx.createSerializer().text<VSIZE>(t1);
|
||||
ctx.createDeserializer().text<VSIZE>(res);
|
||||
|
||||
EXPECT_THAT(res, ContainerEq(t1));
|
||||
}
|
||||
|
||||
TEST(SerializerText, BasicStringAsBufferIsThreadedAsNullTerminatedString) {
|
||||
SerializationContext ctx;
|
||||
std::wstring t1(L"some random text\0xxxxxx", 20);
|
||||
std::wstring wres;
|
||||
constexpr auto VSIZE = sizeof(std::wstring::value_type);
|
||||
|
||||
ctx.createSerializer().text<VSIZE>(t1);
|
||||
ctx.createDeserializer().text<VSIZE>(wres);
|
||||
|
||||
EXPECT_THAT(wres, StrEq(t1));
|
||||
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));
|
||||
}
|
||||
|
||||
const int CARR_LENGTH = 10;
|
||||
|
||||
TEST(SerializerText, CArraySerializesTextLength) {
|
||||
SerializationContext ctx;
|
||||
char t1[CARR_LENGTH]{"some text"};
|
||||
char r1[CARR_LENGTH]{};
|
||||
|
||||
ctx.createSerializer().text(t1);
|
||||
ctx.createDeserializer().text(r1);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(CARR_LENGTH) +
|
||||
std::char_traits<char>::length(t1)));
|
||||
|
||||
EXPECT_THAT(r1, StrEq(t1));
|
||||
EXPECT_THAT(r1, ContainerEq(t1));
|
||||
|
||||
//zero length string
|
||||
t1[0] = 0;
|
||||
SerializationContext ctx2;
|
||||
ctx2.createSerializer().text(t1);
|
||||
ctx2.createDeserializer().text(r1);
|
||||
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(ctx2.containerSizeSerializedBytesCount(CARR_LENGTH)));
|
||||
EXPECT_THAT(r1, StrEq(t1));
|
||||
EXPECT_THAT(r1, ContainerEq(t1));
|
||||
}
|
||||
|
||||
TEST(SerializerText, WhenCArrayWithLargerTypeThenSetSizeExplicitly) {
|
||||
SerializationContext ctx;
|
||||
char32_t t1[10]{U"some text"};
|
||||
char32_t r1[10]{};
|
||||
constexpr auto SIZE = sizeof(char32_t);
|
||||
ctx.createSerializer().text<SIZE>(t1);
|
||||
ctx.createDeserializer().text<SIZE>(r1);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(CARR_LENGTH) +
|
||||
std::char_traits<char32_t>::length(t1) * SIZE));
|
||||
EXPECT_THAT(r1, ContainerEq(t1));
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializerText, WhenCArrayNotNullterminatedThenMakeItNullterminated) {
|
||||
SerializationContext ctx;
|
||||
char t1[CARR_LENGTH]{"some text"};
|
||||
//make last character not nullterminated
|
||||
t1[CARR_LENGTH-1] = 'x';
|
||||
char r1[CARR_LENGTH]{};
|
||||
|
||||
ctx.createSerializer().text(t1);
|
||||
ctx.createDeserializer().text(r1);
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(CARR_LENGTH) +
|
||||
CARR_LENGTH - 1));
|
||||
EXPECT_THAT(r1[CARR_LENGTH-1], Eq(0));
|
||||
}
|
||||
@@ -57,9 +57,9 @@ 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); });
|
||||
s.array(o.arr, writeInt);
|
||||
s.array(o.carr, writeInt);
|
||||
s.container(o.vx, [&s](auto& v) { s.object(v); });
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ TEST(DeltaSerializer, GeneralConceptTest) {
|
||||
BufferWriter bw{ buf };
|
||||
DeltaSerializer<BufferWriter, Y> ser(bw, y, yNew);
|
||||
serialize(ser, yNew);
|
||||
bw.Flush();
|
||||
bw.flush();
|
||||
|
||||
BufferReader br{ buf };
|
||||
DeltaDeserializer<BufferReader, Y> des(br, y, yRead);
|
||||
|
||||
@@ -19,7 +19,7 @@ bool SerializeDeserializeValue(const T& v) {
|
||||
BufferWriter bw{buf};
|
||||
Serializer<BufferWriter> ser(bw);
|
||||
ser.value(v);
|
||||
bw.Flush();
|
||||
bw.flush();
|
||||
|
||||
BufferReader br{buf};
|
||||
Deserializer<BufferReader> des(br);
|
||||
@@ -47,9 +47,27 @@ TEST(SerializerValues, EnumTypes) {
|
||||
EXPECT_THAT(SerializeDeserializeValue(E3::C3), Eq(true));
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializerValues, FloatingPointTypes) {
|
||||
EXPECT_THAT(SerializeDeserializeValue(-484.465), Eq(true));
|
||||
EXPECT_THAT(SerializeDeserializeValue(0.00000015f), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializerValues, ExplicitTypeSize) {
|
||||
int v{23472};
|
||||
constexpr size_t TSIZE = sizeof(v);
|
||||
std::vector<uint8_t> buf{};
|
||||
|
||||
BufferWriter bw{buf};
|
||||
Serializer<BufferWriter> ser(bw);
|
||||
ser.value<TSIZE>(v);
|
||||
bw.flush();
|
||||
|
||||
BufferReader br{buf};
|
||||
Deserializer<BufferReader> des(br);
|
||||
int res;
|
||||
des.value<TSIZE>(res);
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(TSIZE, Eq(buf.size()));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user