From ef278277e32a81a81796f7b20a4790a790c4f6f2 Mon Sep 17 00:00:00 2001 From: fraillt Date: Mon, 24 Jul 2017 16:08:33 +0300 Subject: [PATCH] added endianness support --- CHANGELOG.md | 1 + CMakeLists.txt | 19 +- examples/CMakeLists.txt | 11 +- ext/CodeCoverage.cmake | 3 +- ext/LinkTestLIb.cmake | 18 ++ ext/gtest/CMakeLists.txt | 42 +-- include/bitsery/buffer_reader.h | 100 ++++---- include/bitsery/buffer_writer.h | 124 +++++---- include/bitsery/common.h | 204 +-------------- include/bitsery/delta_deserializer.h | 6 +- include/bitsery/delta_serializer.h | 8 +- include/bitsery/deserializer.h | 142 +++-------- include/bitsery/details/buffer_common.h | 96 +++++++ .../bitsery/details/serialization_common.h | 239 ++++++++++++++++++ include/bitsery/serializer.h | 132 ++++------ tests/CMakeLists.txt | 79 ++---- tests/buffer_bits_op.cpp | 78 ++++-- tests/buffer_bytes_op.cpp | 65 ++++- tests/buffer_endianness.cpp | 183 ++++++++++++++ tests/serialization_container.cpp | 50 ++-- tests/serialization_ext_optional.cpp | 17 +- tests/serialization_objects.cpp | 2 +- tests/serialization_range.cpp | 12 +- tests/serialization_test_utils.h | 10 +- tests/serialization_values.cpp | 2 +- 25 files changed, 1006 insertions(+), 637 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 ext/LinkTestLIb.cmake create mode 100644 include/bitsery/details/buffer_common.h create mode 100644 include/bitsery/details/serialization_common.h create mode 100644 tests/buffer_endianness.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4287ca8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b1c1c70..d33bee2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,12 @@ cmake_minimum_required(VERSION 3.2) -set(PROJECT_NAME bitsery) -set(TEST_PROJECT_NAME ${PROJECT_NAME}_tests) -project(${PROJECT_NAME}) +project(bitsery) - -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() +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) add_subdirectory(examples) -#add tests enable_testing() add_subdirectory(tests) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b6b4b2b..43cec9c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -25,14 +25,11 @@ cmake_minimum_required(VERSION 3.2) include_directories(${CMAKE_SOURCE_DIR}/include) -file(GLOB EXAMPLE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +file(GLOB ExampleFiles ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -FOREACH(EXAMPLE ${EXAMPLE_FILES}) - get_filename_component(EXAMPLE_NAME ${EXAMPLE} NAME_WE) - add_executable(${EXAMPLE_NAME} ${EXAMPLE}) - - set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD 14) - set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) +FOREACH(ExampleFile ${ExampleFiles}) + get_filename_component(ExampleName ${ExampleFile} NAME_WE) + add_executable(${ExampleName} ${ExampleFile}) ENDFOREACH() diff --git a/ext/CodeCoverage.cmake b/ext/CodeCoverage.cmake index a0b0ef5..d6697af 100644 --- a/ext/CodeCoverage.cmake +++ b/ext/CodeCoverage.cmake @@ -146,7 +146,8 @@ FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) # 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} + #extract only /include/bitsery directory + COMMAND ${LCOV_PATH} --extract ${coverage_info} '*include/bitsery*' --output-file ${coverage_cleaned} COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned} COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned} diff --git a/ext/LinkTestLIb.cmake b/ext/LinkTestLIb.cmake new file mode 100644 index 0000000..85c2faa --- /dev/null +++ b/ext/LinkTestLIb.cmake @@ -0,0 +1,18 @@ +function(LinkTestLib TargetName) + + add_dependencies(${TargetName} googletest) + + if(NOT WIN32 OR MINGW) + FOREACH(LibName ${GTestLinkLibNames}) + target_link_libraries(${TargetName} ${GTestLibsDir}/lib${LibName}.a ) + ENDFOREACH() + else() + FOREACH(LibName ${GTestLinkLibNames}) + target_link_libraries(${TargetName} + debug ${GTestLibsDir}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LibName}${CMAKE_FIND_LIBRARY_SUFFIXES} + optimized ${GTestLibsDir}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LibName}${CMAKE_FIND_LIBRARY_SUFFIXES}) + ENDFOREACH() + endif() + + target_link_libraries(${TargetName} ${CMAKE_THREAD_LIBS_INIT}) +endfunction(LinkTestLib) \ No newline at end of file diff --git a/ext/gtest/CMakeLists.txt b/ext/gtest/CMakeLists.txt index 2c26111..e4f0e7a 100644 --- a/ext/gtest/CMakeLists.txt +++ b/ext/gtest/CMakeLists.txt @@ -24,32 +24,32 @@ cmake_minimum_required(VERSION 3.2) project(gtest_builder C CXX) include(ExternalProject) -set(GTEST_FORCE_SHARED_CRT ON) -set(GTEST_DISABLE_PTHREADS OFF) +set(ForceSharedCrt ON) +set(DisablePThreads OFF) if(MINGW) - set(GTEST_DISABLE_PTHREADS ON) + set(DisablePThreads ON) endif() -if (${USE_GMOCK}) +if (${UseGMock}) message("use gmock") - set(BUILD_ARGS -DBUILD_GTEST=OFF -DBUILD_GMOCK=ON) + set(BuildArgs -DBUILD_GTEST=OFF -DBUILD_GMOCK=ON) else () message("use gtest only") - set(BUILD_ARGS -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF) + set(BuildArgs -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF) endif() if (WIN32 AND NOT MINGW) - set(BUILD_ARGS ${BUILD_ARGS} + set(BuildArgs ${BuildArgs} -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} + CMAKE_ARGS ${BuildArgs} + -Dgtest_force_shared_crt=${ForceSharedCrt} + -Dgtest_disable_pthreads=${DisablePThreads} PREFIX "${CMAKE_CURRENT_BINARY_DIR}" # disable update command UPDATE_COMMAND "" @@ -60,20 +60,20 @@ ExternalProject_Add(googletest #export variables ExternalProject_Get_Property(googletest source_dir) ExternalProject_Get_Property(googletest binary_dir) -if (${USE_GMOCK}) +if (${UseGMock}) # 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) + set(GTestIncludeDirs ${source_dir}/googlemock/include ${source_dir}/googletest/include PARENT_SCOPE) + set(GTestLibsDir ${binary_dir}/googlemock PARENT_SCOPE) + set(GTestLibName gmock PARENT_SCOPE) + set(GTestMainLibName gmock_main PARENT_SCOPE) + set(GTestLinkLibNames 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) + set(GTestIncludeDirs ${source_dir}/googletest/include PARENT_SCOPE) + set(GTestLibsDir ${binary_dir}/googletest PARENT_SCOPE) + set(GTestLibName gtest PARENT_SCOPE) + set(GTestMainLibName gtest_main PARENT_SCOPE) # need to include both libs gtest and gtest_main - set(GTEST_LINK_LIBNAMES gtest gtest_main PARENT_SCOPE) + set(GTestLinkLibNames gtest gtest_main PARENT_SCOPE) endif() diff --git a/include/bitsery/buffer_reader.h b/include/bitsery/buffer_reader.h index 6fce072..a4cd3cb 100644 --- a/include/bitsery/buffer_reader.h +++ b/include/bitsery/buffer_reader.h @@ -32,27 +32,32 @@ namespace bitsery { - struct BufferReader { + template + struct BasicBufferReader { + using ValueType = typename Config::BufferValueType; + using ScratchType = typename Config::BufferScrathType; - using value_type = uint8_t; - - BufferReader(const std::vector &buf) : _pos{buf.data()}, _end{buf.data() + buf.size()} { - - } - - BufferReader(const uint8_t* data, size_t size) : _pos{data}, _end{data + size} + BasicBufferReader(const ValueType* data, size_t size) : _pos{data}, _end{data + size} { + static_assert(std::is_unsigned(), "Config::BufferValueType must be unsigned"); + static_assert(std::is_unsigned(), "Config::BufferScrathType must be unsigned"); + static_assert(sizeof(ValueType)*2 == sizeof(ScratchType), "ScratchType must be 2x bigger than value type"); + static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte"); } + + explicit BasicBufferReader(const std::vector &buf) : BasicBufferReader(buf.data(), buf.size()) { + } + template - BufferReader(const uint8_t (&data)[N]): _pos{data}, _end{data + N} + explicit BasicBufferReader(const ValueType (&data)[N]): BasicBufferReader(data, N) { } - BufferReader(const BufferReader&) = delete; - BufferReader& operator=(const BufferReader& ) = delete; - BufferReader(BufferReader&&) noexcept = default; - BufferReader& operator=(BufferReader&&) noexcept = default; - ~BufferReader() noexcept = default; + BasicBufferReader(const BasicBufferReader&) = delete; + BasicBufferReader& operator=(const BasicBufferReader& ) = delete; + BasicBufferReader(BasicBufferReader&&) noexcept = default; + BasicBufferReader& operator=(BasicBufferReader&&) noexcept = default; + ~BasicBufferReader() noexcept = default; template @@ -62,7 +67,7 @@ namespace bitsery { using UT = typename std::make_unsigned::type; return !m_scratch ? directRead(&v, 1) - : readBits(reinterpret_cast(v), BITS_SIZE); + : readBits(reinterpret_cast(v), details::BITS_SIZE); } template @@ -70,16 +75,15 @@ namespace bitsery { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (!m_scratchBits) { + if (!m_scratchBits) return directRead(buf, count); - } else { - using UT = typename std::make_unsigned::type; - //todo improve implementation - const auto end = buf + count; - for (auto it = buf; it != end; ++it) { - if (!readBits(reinterpret_cast(*it), BITS_SIZE)) - return false; - } + + using UT = typename std::make_unsigned::type; + //todo improve implementation + const auto end = buf + count; + for (auto it = buf; it != end; ++it) { + if (!readBits(reinterpret_cast(*it), details::BITS_SIZE)) + return false; } return true; } @@ -88,7 +92,7 @@ namespace bitsery { template bool readBits(T &v, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(bitsCount <= BITS_SIZE); + assert(bitsCount <= details::BITS_SIZE); const auto bytesRequired = bitsCount > m_scratchBits ? ((bitsCount - 1 - m_scratchBits) >> 3) + 1u @@ -101,8 +105,8 @@ namespace bitsery { bool align() { if (m_scratchBits) { - SCRATCH_TYPE tmp{}; - readBitsInternal(tmp, BITS_SIZE - m_scratchBits); + ScratchType tmp{}; + readBitsInternal(tmp, m_scratchBits); return tmp == 0; } return true; @@ -113,8 +117,8 @@ namespace bitsery { } private: - const value_type* _pos; - const value_type* _end; + const ValueType* _pos; + const ValueType* _end; template bool directRead(T *v, size_t count) { @@ -122,28 +126,39 @@ namespace bitsery { const auto bytesCount = sizeof(T) * count; if (static_cast(std::distance(_pos, _end)) < bytesCount) return false; - std::copy_n(_pos, bytesCount, reinterpret_cast(v)); + //read from buffer, to data ptr, + std::copy_n(_pos, bytesCount, reinterpret_cast(v)); std::advance(_pos, bytesCount); + //swap each byte if nessesarry + _swapDataBits(v, count, std::integral_constant{}); return true; } + template + void _swapDataBits(T *v, size_t count, std::true_type) { + std::for_each(v, std::next(v, count), [this](T& v) { v = details::swap(v); }); + } + + template + void _swapDataBits(T *v, size_t count, std::false_type) { + //empty function because no swap is required + } + template void readBitsInternal(T &v, size_t size) { auto bitsLeft = size; T res{}; while (bitsLeft > 0) { - auto bits = std::min(bitsLeft, BITS_SIZE); + auto bits = std::min(bitsLeft, details::BITS_SIZE); if (m_scratchBits < bits) { - value_type tmp; - - std::copy_n(_pos, 1, reinterpret_cast(&tmp)); - std::advance(_pos, 1); - - m_scratch |= static_cast(tmp) << m_scratchBits; - m_scratchBits += BITS_SIZE; + ValueType tmp; + directRead(&tmp, 1); + m_scratch |= static_cast(tmp) << m_scratchBits; + m_scratchBits += details::BITS_SIZE; } auto shiftedRes = - static_cast(m_scratch & ((static_cast(1) << bits) - 1)) << (size - bitsLeft); + static_cast(m_scratch & ((static_cast(1) << bits) - 1)) << (size - bitsLeft); res |= shiftedRes; m_scratch >>= bits; m_scratchBits -= bits; @@ -152,13 +167,12 @@ namespace bitsery { v = res; } - using SCRATCH_TYPE = typename BIGGER_TYPE::type; - - SCRATCH_TYPE m_scratch{}; + ScratchType 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. }; - + //helper type + using BufferReader = BasicBufferReader; } #endif //BITSERY_BUFFER_READER_H diff --git a/include/bitsery/buffer_writer.h b/include/bitsery/buffer_writer.h index b84da1e..e6c8840 100644 --- a/include/bitsery/buffer_writer.h +++ b/include/bitsery/buffer_writer.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace bitsery { @@ -38,13 +39,13 @@ namespace bitsery { void writeBytes(const T &) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bitsCount += BITS_SIZE; + _bitsCount += details::BITS_SIZE; } template void writeBits(const T &, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(bitsCount <= BITS_SIZE); + assert(bitsCount <= details::BITS_SIZE); _bitsCount += bitsCount; } @@ -52,7 +53,7 @@ namespace bitsery { void writeBuffer(const T *, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bitsCount += BITS_SIZE * count; + _bitsCount += details::BITS_SIZE * count; } //get size in bytes @@ -65,30 +66,34 @@ namespace bitsery { }; + template + struct BasicBufferWriter { + using ValueType = typename Config::BufferValueType; + using ScratchType = typename Config::BufferScrathType; - struct BufferWriter { - using value_type = uint8_t; - - explicit BufferWriter(std::vector &buffer) : _buf{buffer}, _outIt{std::back_inserter(buffer)} { - static_assert(std::is_unsigned::value, ""); + explicit BasicBufferWriter(std::vector &buffer) : _outIt{std::back_inserter(buffer)} { + static_assert(std::is_unsigned(), "Config::BufferValueType must be unsigned"); + static_assert(std::is_unsigned(), "Config::BufferScrathType must be unsigned"); + static_assert(sizeof(ValueType)*2 == sizeof(ScratchType), "ScratchType must be 2x bigger than value type"); + static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte"); } - BufferWriter(const BufferWriter&) = delete; - BufferWriter& operator=(const BufferWriter& ) = delete; - BufferWriter(BufferWriter&&) noexcept = default; - BufferWriter& operator=(BufferWriter&&) noexcept = default; - ~BufferWriter() noexcept = default; + BasicBufferWriter(const BasicBufferWriter&) = delete; + BasicBufferWriter& operator=(const BasicBufferWriter& ) = delete; + BasicBufferWriter(BasicBufferWriter&&) noexcept = default; + BasicBufferWriter& operator=(BasicBufferWriter&&) noexcept = default; + ~BasicBufferWriter() noexcept = default; template void writeBytes(const T &v) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (!m_scratchBits) { + if (!_scratchBits) { directWrite(&v, 1); } else { using UT = typename std::make_unsigned::type; - writeBits(reinterpret_cast(v), BITS_SIZE); + writeBits(reinterpret_cast(v), details::BITS_SIZE); } } @@ -96,36 +101,36 @@ namespace bitsery { void writeBuffer(const T *buf, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (!m_scratchBits) { + if (!_scratchBits) { directWrite(buf, count); } else { using UT = typename std::make_unsigned::type; //todo improve implementation const auto end = buf + count; for (auto it = buf; it != end; ++it) - writeBits(reinterpret_cast(*it), BITS_SIZE); + writeBits(reinterpret_cast(*it), details::BITS_SIZE); } } template void writeBits(const T &v, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(bitsCount <= BITS_SIZE); + assert(0 < bitsCount && bitsCount <= details::BITS_SIZE); assert(v <= ((1ULL << bitsCount) - 1)); writeBitsInternal(v, bitsCount); } void align() { - if (m_scratchBits) - writeBitsInternal(value_type{}, BITS_SIZE - m_scratchBits); + if (_scratchBits) + writeBitsInternal(ValueType{}, details::BITS_SIZE - _scratchBits); } void flush() { - if (m_scratchBits) { - auto tmp = static_cast( m_scratch & bufTypeMask ); + if (_scratchBits) { + auto tmp = static_cast( _scratch & _MASK ); directWrite(&tmp, 1); - m_scratch >>= m_scratchBits; - m_scratchBits -= m_scratchBits; + _scratch >>= _scratchBits; + _scratchBits -= _scratchBits; } } @@ -133,57 +138,68 @@ namespace bitsery { private: template - 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(v), bytesSize, _buf.data() + pos); + void directWrite(T&& v, size_t count) { + _directWriteSwapTag(std::forward(v), count, std::integral_constant{}); + } + + template + void _directWriteSwapTag(const T *v, size_t count, std::true_type) { + std::for_each(v, std::next(v, count), [this](const T& v) { + const auto res = details::swap(v); + std::copy_n(reinterpret_cast(&res), sizeof(T), _outIt); + }); + } + + template + void _directWriteSwapTag(const T *v, size_t count, std::false_type) { + std::copy_n(reinterpret_cast(v), count * sizeof(T), _outIt); } template void writeBitsInternal(const T &v, size_t size) { + constexpr size_t valueSize = details::BITS_SIZE; auto value = v; auto bitsLeft = size; while (bitsLeft > 0) { - auto bits = std::min(bitsLeft, BITS_SIZE); - m_scratch |= static_cast( value ) << m_scratchBits; - m_scratchBits += bits; - if (m_scratchBits >= BITS_SIZE) { - auto tmp = static_cast(m_scratch & bufTypeMask); + auto bits = std::min(bitsLeft, valueSize); + _scratch |= static_cast( value ) << _scratchBits; + _scratchBits += bits; + if (_scratchBits >= valueSize) { + auto tmp = static_cast(_scratch & _MASK); directWrite(&tmp, 1); - m_scratch >>= BITS_SIZE; - m_scratchBits -= BITS_SIZE; + _scratch >>= valueSize; + _scratchBits -= valueSize; - value >>= BITS_SIZE; + value >>= valueSize; } bitsLeft -= bits; } } - void writeBitsInternal(const value_type &v, size_t size) { + + //overload for ValueType, for better performance + void writeBitsInternal(const ValueType &v, size_t size) { if (size > 0) { - m_scratch |= static_cast( v ) << m_scratchBits; - m_scratchBits += size; - if (m_scratchBits >= BITS_SIZE) { - auto tmp = static_cast(m_scratch & bufTypeMask); + _scratch |= static_cast( v ) << _scratchBits; + _scratchBits += size; + if (_scratchBits >= details::BITS_SIZE) { + auto tmp = static_cast(_scratch & _MASK); directWrite(&tmp, 1); - m_scratch >>= BITS_SIZE; - m_scratchBits -= BITS_SIZE; + _scratch >>= details::BITS_SIZE; + _scratchBits -= details::BITS_SIZE; } } } - const value_type bufTypeMask = 0xFF; - using SCRATCH_TYPE = typename BIGGER_TYPE::type; - std::vector &_buf; - std::back_insert_iterator> _outIt; - SCRATCH_TYPE m_scratch{}; - size_t m_scratchBits{}; - - - //size_t _bufSize{}; - + const ValueType _MASK = std::numeric_limits::max(); + std::back_insert_iterator> _outIt; + ScratchType _scratch{}; + size_t _scratchBits{}; }; + + //helper type + using BufferWriter = BasicBufferWriter; } #endif //BITSERY_BUFFER_WRITER_H diff --git a/include/bitsery/common.h b/include/bitsery/common.h index 5c585ac..36ccbf5 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -21,215 +21,27 @@ //SOFTWARE. - #ifndef BITSERY_COMMON_H #define BITSERY_COMMON_H -#include +#include "bitsery/details/buffer_common.h" namespace bitsery { - template - constexpr size_t BITS_SIZE = sizeof(T) << 3; - - template - struct BIGGER_TYPE { + struct DefaultConfig { + static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian; + using BufferValueType = uint8_t; + using BufferScrathType = uint16_t; }; - template<> - struct BIGGER_TYPE { - typedef uint16_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef uint32_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef uint64_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef int16_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef int32_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef int64_t type; - }; - - template<> - struct BIGGER_TYPE { - typedef int16_t type; - }; - - - template - struct SAME_SIZE_UNSIGNED_TYPE { - typedef std::make_unsigned_t type; - }; - - template - struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { - typedef std::make_unsigned_t> type; - }; - - template - struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { - typedef std::conditional_t::value, uint32_t, uint64_t> type; - }; - - template - using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE::type; - - - template - struct ProcessAnyType { - template - static void serialize(S &s, T &&v) { - s.template value(std::forward(v)); - } - }; - - template<> - struct ProcessAnyType<0> { - template - static void serialize(S &s, T &&v) { - s.object(std::forward(v)); - } - }; +/* + * serializer macro, serialize function specialization that accepts T& and const T& + */ #define SERIALIZE(ObjectType) \ template ::value || std::is_same::value>::type* = nullptr> \ S& serialize(S& s, T& o) -/* - * range functions - */ - - template - constexpr size_t calcRequiredBits(T min, T max) { - size_t res{}; - for (auto diff = max - min; diff > 0; diff >>= 1) - ++res; - return res; - } - - - template - struct RangeSpec { - - constexpr RangeSpec(T minValue, T maxValue) - : min{minValue}, - max{maxValue}, - bitsRequired{calcRequiredBits(min, max)} { - } - - const T min; - const T max; - const size_t bitsRequired; - }; - - - template - struct RangeSpec::value>::type> { - - constexpr RangeSpec(T minValue, T maxValue) : - min{minValue}, - max{maxValue}, - bitsRequired{calcRequiredBits( - static_cast>(min), - static_cast>(max))} { - } - - const T min; - const T max; - const size_t bitsRequired; - }; - -//this class is used to make default RangeSpec float specialization always prefer constructor with precision - struct BitsConstraint { - explicit constexpr BitsConstraint(size_t bits) : value{bits} {} - - const size_t value; - }; - - template - struct RangeSpec::value>::type> { - - constexpr RangeSpec(T minValue, T maxValue, BitsConstraint bits) : - min{minValue}, - max{maxValue}, - bitsRequired{bits.value} { - } - - constexpr RangeSpec(T minValue, T maxValue, T precision) : - min{minValue}, - max{maxValue}, - bitsRequired{calcRequiredBits>({}, ((max - min) / precision))} { - - } - - const T min; - const T max; - const size_t bitsRequired; - }; - - template::value>::type * = nullptr> - bool isRangeValid(const T &v, const RangeSpec &r) { - return !(r.min > v || v > r.max); - } - - template::value>::type * = nullptr> - bool isRangeValid(const T &v, const RangeSpec &r) { - using VT = std::underlying_type_t; - return !(static_cast(r.min) > static_cast(v) - || static_cast(v) > static_cast(r.max)); - } - -/* - * delta functions - */ - - class ObjectMemoryPosition { - public: - - template - ObjectMemoryPosition(const T &oldObj, const T &newObj) - :ObjectMemoryPosition{reinterpret_cast(&oldObj), reinterpret_cast(&newObj), - sizeof(T)} { - } - - template - bool isFieldsEquals(const T &newObjField) { - return *getOldObjectField(newObjField) == newObjField; - } - - template - const T *getOldObjectField(const T &field) { - auto offset = reinterpret_cast(&field) - newObj; - return reinterpret_cast(oldObj + offset); - } - - private: - - ObjectMemoryPosition(const char *objOld, const char *objNew, size_t) - : oldObj{objOld}, - newObj{objNew} { - } - - const char *oldObj; - const char *newObj; - }; - } #endif //BITSERY_COMMON_H diff --git a/include/bitsery/delta_deserializer.h b/include/bitsery/delta_deserializer.h index 276a5bf..f09e72d 100644 --- a/include/bitsery/delta_deserializer.h +++ b/include/bitsery/delta_deserializer.h @@ -40,7 +40,7 @@ namespace bitsery { _reader{r}, _oldObj{oldObj}, _newObj{newObj}, - _objMemPos(std::deque(1, ObjectMemoryPosition{oldObj, newObj})), + _objMemPos(std::deque(1, details::ObjectMemoryPosition{oldObj, newObj})), _isNewElement{false} { }; @@ -134,7 +134,7 @@ namespace bitsery { const TObj &_oldObj; const TObj &_newObj; - std::stack _objMemPos; + std::stack _objMemPos; bool _isNewElement; template @@ -173,7 +173,7 @@ namespace bitsery { *p = *pOld; --offset; } else { - _objMemPos.emplace(ObjectMemoryPosition{*pOld, *p}); + _objMemPos.emplace(details::ObjectMemoryPosition{*pOld, *p}); fnc(*this, *p); _objMemPos.pop(); offset = readIndexOffset(); diff --git a/include/bitsery/delta_serializer.h b/include/bitsery/delta_serializer.h index 802baf8..2ad1360 100644 --- a/include/bitsery/delta_serializer.h +++ b/include/bitsery/delta_serializer.h @@ -40,7 +40,7 @@ namespace bitsery { _writter{w}, _oldObj{oldObj}, _newObj{newObj}, - _objMemPos(std::deque(1, ObjectMemoryPosition{oldObj, newObj})), + _objMemPos(std::deque(1, details::ObjectMemoryPosition{oldObj, newObj})), _isNewElement{false} { }; @@ -132,7 +132,7 @@ namespace bitsery { Writter &_writter; const TObj &_oldObj; const TObj &_newObj; - std::stack _objMemPos; + std::stack _objMemPos; bool _isNewElement; template @@ -170,7 +170,7 @@ namespace bitsery { auto lastChanged = begin; while (misMatch.first != oldEnd && misMatch.second != end) { writeIndexOffset(std::distance(lastChanged, misMatch.second)); - _objMemPos.emplace(ObjectMemoryPosition{*misMatch.first, *misMatch.second}); + _objMemPos.emplace(details::ObjectMemoryPosition{*misMatch.first, *misMatch.second}); fnc(*this, *misMatch.second); _objMemPos.pop(); ++misMatch.first; @@ -183,7 +183,7 @@ namespace bitsery { writeIndexOffset(std::distance(lastChanged, end)); //write old elements for (auto pOld = misMatch.first; p != end && pOld != oldEnd; ++p, ++pOld) { - _objMemPos.emplace(ObjectMemoryPosition{*pOld, *p}); + _objMemPos.emplace(details::ObjectMemoryPosition{*pOld, *p}); fnc(*this, *p); _objMemPos.pop(); } diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index bd0636c..d36f1bb 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -25,33 +25,11 @@ #define BITSERY_DESERIALIZER_H #include "common.h" -#include +#include "details/serialization_common.h" #include namespace bitsery { - /* - * functions for range - */ - template::value>::type* = nullptr> - void setRangeValue(T& v, const RangeSpec& r) { - v += r.min; - }; - - template::value>::type* = nullptr> - void setRangeValue(T& v, const RangeSpec& r) { - using VT = std::underlying_type_t; - reinterpret_cast(v) += static_cast(r.min); - }; - - template::value>::type* = nullptr> - void setRangeValue(T& v, const RangeSpec& r) { - using UIT = SAME_SIZE_UNSIGNED; - const auto intRep = reinterpret_cast(v); - const UIT maxUint = (static_cast(1) << r.bitsRequired) - 1; - v = r.min + (static_cast(intRep) / maxUint) * (r.max - r.min); - }; - template class Deserializer { @@ -63,6 +41,7 @@ namespace bitsery { return serialize(*this, std::forward(obj)); } + //in c++17 change "class" to typename template