diff --git a/.github/workflows/on_linux.yml b/.github/workflows/on_linux.yml index ea8a340..94d3ae6 100644 --- a/.github/workflows/on_linux.yml +++ b/.github/workflows/on_linux.yml @@ -53,10 +53,10 @@ jobs: sudo add-apt-repository ppa:team-xbmc/ppa sudo apt-get update sudo apt-get install libgmock-dev - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Configure run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=${{ matrix.config.cxx_ver }} - name: Build run: cmake --build build - name: Run tests - run: ctest --test-dir build/tests + run: ctest --test-dir build diff --git a/.github/workflows/on_mac.yml b/.github/workflows/on_mac.yml index b929546..be59fcb 100644 --- a/.github/workflows/on_mac.yml +++ b/.github/workflows/on_mac.yml @@ -17,10 +17,10 @@ jobs: git checkout release-1.11.0 cmake -S . -B build cmake --build build --target install - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Configure run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17 - name: Build run: cmake --build build - name: Run tests - run: ctest --test-dir build/tests + run: ctest --test-dir build diff --git a/.github/workflows/on_windows.yml b/.github/workflows/on_windows.yml index 7e91c86..d88185e 100644 --- a/.github/workflows/on_windows.yml +++ b/.github/workflows/on_windows.yml @@ -18,7 +18,7 @@ jobs: git checkout release-1.11.0 cmake -S . -B build -Dgtest_force_shared_crt=ON cmake --build build --config Release --target install - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Configure run: cmake -S . -B build -DBITSERY_BUILD_TESTS=ON -DBITSERY_BUILD_EXAMPLES=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_FLAGS="/Zc:__cplusplus /permissive- /EHsc" env: @@ -26,4 +26,4 @@ jobs: - name: Build run: cmake --build build --config Release - name: Run tests - run: ctest --test-dir build/tests + run: ctest --test-dir build diff --git a/.gitignore b/.gitignore index 911d40c..7fef087 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .idea/ .vs/ +.vscode/ build/ cmake-build-* CTestConfig.cmake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b9762ff..0000000 --- a/.travis.yml +++ /dev/null @@ -1,70 +0,0 @@ -dist: xenial -language: cpp -# CXX_COMPILER and CC_COMPILER is defined, because travis will override CC and CXX environment variables -# We'll need to override them back "before_install" -matrix: - include: - - addons: - apt: - packages: - - g++-5 - env: - - CXXSTD=11 - - CXX_COMPILER=g++-5 - - CC_COMPILER=gcc-5 - - addons: - apt: - packages: - - clang-3.9 - env: - - CXXSTD=11 - - CXX_COMPILER=clang++-3.9 - - CC_COMPILER=clang-3.9 - - addons: - apt: - packages: - - g++-7 - sources: - - ubuntu-toolchain-r-test - env: - - CXXSTD=17 - - CXX_COMPILER=g++-7 - - CC_COMPILER=gcc-7 - - addons: - apt: - packages: - - libstdc++-7-dev - - clang-8 - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-xenial-8 - - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - env: - - CXXSTD=17 - - CXX_COMPILER=clang++-8 - - CC_COMPILER=clang-8 - -before_install: - - export CXX=$CXX_COMPILER - - export CC=$CC_COMPILER - -install: - - wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz - - tar xf release-1.10.0.tar.gz - - cd googletest-release-1.10.0 - - cmake -DBUILD_SHARED_LIBS=ON . - - make - - sudo make install - - cd .. - -before_script: - - mkdir build - - cd build -  - cmake -DBITSERY_BUILD_TESTS=ON -DCMAKE_CXX_STANDARD=$CXXSTD .. - - -script: - - make - - cd tests - - ctest \ No newline at end of file diff --git a/include/bitsery/adapter/buffer.h b/include/bitsery/adapter/buffer.h index 05da5e6..05cd115 100644 --- a/include/bitsery/adapter/buffer.h +++ b/include/bitsery/adapter/buffer.h @@ -23,8 +23,11 @@ #ifndef BITSERY_ADAPTER_BUFFER_H #define BITSERY_ADAPTER_BUFFER_H -#include "../details/adapter_common.h" +#include "../details/adapter_bit_packing.h" #include "../traits/core/traits.h" +#include +#include +#include namespace bitsery { @@ -32,6 +35,8 @@ namespace bitsery { class InputBufferAdapter: public details::InputAdapterBaseCRTP> { public: friend details::InputAdapterBaseCRTP>; + + using BitPackingEnabled = details::InputAdapterBitPackingWrapper>; using TConfig = Config; using TIterator = typename traits::BufferAdapterTraits::type>::TConstIterator; using TValue = typename traits::BufferAdapterTraits::type>::TValue; @@ -108,21 +113,21 @@ namespace bitsery { template void readInternalValue(TValue *data) { - readInternalChecked(data, SIZE, std::integral_constant{}); + readInternalImpl(data, SIZE, std::integral_constant{}); } void readInternalBuffer(TValue *data, size_t size) { - readInternalChecked(data, size, std::integral_constant{}); + readInternalImpl(data, size, std::integral_constant{}); } - void readInternalChecked(TValue *data, size_t size, std::false_type) { + void readInternalImpl(TValue * data, size_t size, std::false_type) { const size_t newOffset = _currOffset + size; assert(newOffset <= _endReadOffset); std::copy_n(_beginIt + static_cast(_currOffset), size, data); _currOffset = newOffset; } - void readInternalChecked(TValue *data, size_t size, std::true_type) { + void readInternalImpl(TValue *data, size_t size, std::true_type) { const size_t newOffset = _currOffset + size; if (newOffset <= _endReadOffset) { std::copy_n(_beginIt + static_cast(_currOffset), size, data); @@ -155,6 +160,7 @@ namespace bitsery { return _currOffset; } + TIterator _beginIt; size_t _currOffset; size_t _endReadOffset; @@ -166,6 +172,8 @@ namespace bitsery { class OutputBufferAdapter: public details::OutputAdapterBaseCRTP> { public: friend details::OutputAdapterBaseCRTP>; + + using BitPackingEnabled = details::OutputAdapterBitPackingWrapper>; using TConfig = Config; using TIterator = typename traits::BufferAdapterTraits::TIterator; using TValue = typename traits::BufferAdapterTraits::TValue; diff --git a/include/bitsery/adapter/measure_size.h b/include/bitsery/adapter/measure_size.h index ba61f41..3f6ef68 100644 --- a/include/bitsery/adapter/measure_size.h +++ b/include/bitsery/adapter/measure_size.h @@ -23,9 +23,9 @@ #ifndef BITSERY_ADAPTER_MEASURE_SIZE_H #define BITSERY_ADAPTER_MEASURE_SIZE_H +#include "../details/adapter_bit_packing.h" #include #include -#include "../details/adapter_common.h" namespace bitsery { @@ -34,7 +34,7 @@ namespace bitsery { class BasicMeasureSize { public: - static constexpr bool BitPackingEnabled = true; + using BitPackingEnabled = details::BasicMeasureSizeBitPackingWrapper>; using TConfig = Config; using TValue = void; @@ -42,53 +42,42 @@ namespace bitsery { void writeBytes(const T&) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _currPosBits += details::BitsSize::value; + _currPos += SIZE; } template void writeBuffer(const T*, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _currPosBits += details::BitsSize::value * count; - } - - template - void writeBits(const T&, size_t bitsCount) { - static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(bitsCount <= details::BitsSize::value); - _currPosBits += bitsCount; + _currPos += SIZE * count; } void currentWritePos(size_t pos) { - align(); - const auto newPos = pos * 8; - if (_currPosBits > newPos) - _prevLargestPos = _currPosBits; - _currPosBits = newPos; + const auto maxPos = _currPos > pos ? _currPos : pos; + if (maxPos > _biggestCurrentPos) { + _biggestCurrentPos = maxPos; + } + _currPos = pos; } size_t currentWritePos() const { - return _currPosBits / 8; + return _currPos; } void align() { - auto _scratch = (_currPosBits % 8); - _currPosBits += (8 - _scratch) % 8; } void flush() { - align(); } //get size in bytes size_t writtenBytesCount() const { - const auto max = _currPosBits > _prevLargestPos ? _currPosBits : _prevLargestPos; - return max / 8; + return _currPos > _biggestCurrentPos ? _currPos : _biggestCurrentPos; } private: - size_t _prevLargestPos{}; - size_t _currPosBits{}; + size_t _biggestCurrentPos{}; + size_t _currPos{}; }; //helper type for default config diff --git a/include/bitsery/adapter/stream.h b/include/bitsery/adapter/stream.h index 5632761..371bf92 100644 --- a/include/bitsery/adapter/stream.h +++ b/include/bitsery/adapter/stream.h @@ -23,8 +23,10 @@ #ifndef BITSERY_ADAPTER_STREAM_H #define BITSERY_ADAPTER_STREAM_H -#include "../details/adapter_common.h" +#include "../details/adapter_bit_packing.h" #include "../traits/array.h" +#include +#include #include #include @@ -34,6 +36,8 @@ namespace bitsery { class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP> { public: friend details::InputAdapterBaseCRTP>; + + using BitPackingEnabled = details::InputAdapterBitPackingWrapper>; using TConfig = Config; using TValue = TChar; @@ -117,6 +121,8 @@ namespace bitsery { class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP> { public: friend details::OutputAdapterBaseCRTP>; + + using BitPackingEnabled = details::OutputAdapterBitPackingWrapper>; using TConfig = Config; using TValue = TChar; @@ -161,6 +167,8 @@ namespace bitsery { public details::OutputAdapterBaseCRTP> { public: friend details::OutputAdapterBaseCRTP>; + + using BitPackingEnabled = details::OutputAdapterBitPackingWrapper>; using TConfig = Config; using Buffer = TBuffer; using BufferIt = typename traits::BufferAdapterTraits::TIterator; @@ -243,7 +251,7 @@ namespace bitsery { } else { writeBufferToStream(); // write buffer directly to stream - _ostream->rdbuf()->sputn(data, size); + _ostream->rdbuf()->sputn(data, static_cast(size)); } } diff --git a/include/bitsery/brief_syntax/map.h b/include/bitsery/brief_syntax/map.h index 5e776a2..e67713f 100644 --- a/include/bitsery/brief_syntax/map.h +++ b/include/bitsery/brief_syntax/map.h @@ -24,9 +24,9 @@ #ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H #define BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H +#include "../ext/std_map.h" #include #include -#include "../ext/std_map.h" namespace bitsery { template diff --git a/include/bitsery/brief_syntax/set.h b/include/bitsery/brief_syntax/set.h index 7d8557d..dbcd190 100644 --- a/include/bitsery/brief_syntax/set.h +++ b/include/bitsery/brief_syntax/set.h @@ -24,9 +24,9 @@ #ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H #define BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H +#include "../ext/std_set.h" #include #include -#include "../ext/std_set.h" namespace bitsery { template diff --git a/include/bitsery/brief_syntax/stack.h b/include/bitsery/brief_syntax/stack.h index 4f7cca9..621125a 100644 --- a/include/bitsery/brief_syntax/stack.h +++ b/include/bitsery/brief_syntax/stack.h @@ -24,8 +24,8 @@ #ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H #define BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H -#include #include "../ext/std_stack.h" +#include namespace bitsery { template diff --git a/include/bitsery/brief_syntax/unordered_map.h b/include/bitsery/brief_syntax/unordered_map.h index 32e7a80..27ec3db 100644 --- a/include/bitsery/brief_syntax/unordered_map.h +++ b/include/bitsery/brief_syntax/unordered_map.h @@ -24,9 +24,9 @@ #ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H #define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H +#include "../ext/std_map.h" #include #include -#include "../ext/std_map.h" namespace bitsery { template diff --git a/include/bitsery/brief_syntax/unordered_set.h b/include/bitsery/brief_syntax/unordered_set.h index 14d9688..dbfc04c 100644 --- a/include/bitsery/brief_syntax/unordered_set.h +++ b/include/bitsery/brief_syntax/unordered_set.h @@ -24,9 +24,9 @@ #ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H #define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H +#include "../ext/std_set.h" #include #include -#include "../ext/std_set.h" namespace bitsery { template diff --git a/include/bitsery/common.h b/include/bitsery/common.h index 7646340..80a97bd 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -24,8 +24,6 @@ #ifndef BITSERY_COMMON_H #define BITSERY_COMMON_H -#include - namespace bitsery { /* diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index 3633ef4..747824f 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -25,150 +25,14 @@ #define BITSERY_DESERIALIZER_H #include "details/serialization_common.h" -#include "details/adapter_common.h" -#include namespace bitsery { - namespace details { - template - class InputAdapterBitPackingWrapper { - public: - - static constexpr bool BitPackingEnabled = true; - using TConfig = typename TAdapter::TConfig; - using TValue = typename TAdapter::TValue; - - InputAdapterBitPackingWrapper(TAdapter& adapter) - : _wrapped{adapter} - { - } - - - ~InputAdapterBitPackingWrapper() { - align(); - } - - template - void readBytes(T &v) { - static_assert(std::is_integral(), ""); - static_assert(sizeof(T) == SIZE, ""); - using UT = typename std::make_unsigned::type; - if (!m_scratchBits) - this->_wrapped.template readBytes(v); - else - readBits(reinterpret_cast(v), details::BitsSize::value); - } - - template - void readBuffer(T *buf, size_t count) { - static_assert(std::is_integral(), ""); - static_assert(sizeof(T) == SIZE, ""); - - if (!m_scratchBits) { - this->_wrapped.template readBuffer(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) - readBits(reinterpret_cast(*it), details::BitsSize::value); - } - } - - template - void readBits(T &v, size_t bitsCount) { - static_assert(std::is_integral() && std::is_unsigned(), ""); - readBitsInternal(v, bitsCount); - } - - void align() { - if (m_scratchBits) { - ScratchType tmp{}; - readBitsInternal(tmp, m_scratchBits); - handleAlignErrors(tmp, std::integral_constant{}); - } - } - - void currentReadPos(size_t pos) { - align(); - this->_wrapped.currentReadPos(pos); - } - - size_t currentReadPos() const { - return this->_wrapped.currentReadPos(); - } - - void currentReadEndPos(size_t pos) { - this->_wrapped.currentReadEndPos(pos); - } - - size_t currentReadEndPos() const { - return this->_wrapped.currentReadEndPos(); - } - - bool isCompletedSuccessfully() const { - return this->_wrapped.isCompletedSuccessfully(); - } - - ReaderError error() const { - return this->_wrapped.error(); - } - - void error(ReaderError error) { - this->_wrapped.error(error); - } - - private: - TAdapter& _wrapped; - using UnsignedValue = typename std::make_unsigned::type; - using ScratchType = typename details::ScratchType::type; - - ScratchType m_scratch{}; - size_t m_scratchBits{}; - - template - void readBitsInternal(T &v, size_t size) { - auto bitsLeft = size; - using TFast = typename FastType::type; - TFast res{}; - while (bitsLeft > 0) { - auto bits = (std::min)(bitsLeft, details::BitsSize::value); - if (m_scratchBits < bits) { - UnsignedValue tmp; - this->_wrapped.template readBytes(tmp); - m_scratch |= static_cast(tmp) << m_scratchBits; - m_scratchBits += details::BitsSize::value; - } - auto shiftedRes = - static_cast(m_scratch & ((static_cast(1) << bits) - 1)) << (size - bitsLeft); - res = static_cast(res | static_cast(shiftedRes)); - m_scratch >>= bits; - m_scratchBits -= bits; - bitsLeft -= bits; - } - v = static_cast(res); - } - - void handleAlignErrors(ScratchType value, std::true_type) { - if (value) - error(ReaderError::InvalidData); - } - - void handleAlignErrors(ScratchType, std::false_type) { - } - - }; - - } - template class Deserializer: public details::AdapterAndContextRef { public: //helper type, that always returns bit-packing enabled type, useful inside deserialize function when enabling bitpacking - using BPEnabledType = Deserializer>::type, TContext>; + using BPEnabledType = Deserializer; using TConfig = typename TInputAdapter::TConfig; using details::AdapterAndContextRef::AdapterAndContextRef; @@ -213,7 +77,7 @@ namespace bitsery { */ template void enableBitPacking(Fnc&& fnc) { - procEnableBitPacking(std::forward(fnc), std::integral_constant{}); + procEnableBitPacking(std::forward(fnc), std::is_same{}); } /* @@ -253,7 +117,7 @@ namespace bitsery { */ void boolValue(bool &v) { procBoolValue(v, - std::integral_constant{}, + std::is_same{}, std::integral_constant{}); } @@ -518,7 +382,6 @@ namespace bitsery { template void procEnableBitPacking(const Fnc& fnc, std::false_type) { - //create deserializer using bitpacking wrapper auto des = createWithContext(std::integral_constant{}); fnc(des); } diff --git a/include/bitsery/details/adapter_bit_packing.h b/include/bitsery/details/adapter_bit_packing.h new file mode 100644 index 0000000..9150c08 --- /dev/null +++ b/include/bitsery/details/adapter_bit_packing.h @@ -0,0 +1,364 @@ +//MIT License +// +//Copyright (c) 2022 Mindaugas Vinkelis +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +#ifndef BITSERY_DETAILS_ADAPTER_BIT_PACKING_H +#define BITSERY_DETAILS_ADAPTER_BIT_PACKING_H + +#include "not_defined_type.h" +#include "./adapter_common.h" +#include "../common.h" +#include + + +namespace bitsery { + + namespace details { + + template + class InputAdapterBitPackingWrapper { + public: + // in order to check if adapter is BP enabled, we use `std::is_same` + // so when current implementation is BP enabled, we always specify current class as BitPackingEnabled. + using BitPackingEnabled = InputAdapterBitPackingWrapper; + using TConfig = typename TAdapter::TConfig; + using TValue = typename TAdapter::TValue; + + InputAdapterBitPackingWrapper(TAdapter& adapter) + : _wrapped{adapter} + { + } + + ~InputAdapterBitPackingWrapper() { + align(); + } + + template + void readBytes(T &v) { + static_assert(std::is_integral(), ""); + static_assert(sizeof(T) == SIZE, ""); + using UT = typename std::make_unsigned::type; + if (!m_scratchBits) + this->_wrapped.template readBytes(v); + else + readBits(reinterpret_cast(v), details::BitsSize::value); + } + + template + void readBuffer(T *buf, size_t count) { + static_assert(std::is_integral(), ""); + static_assert(sizeof(T) == SIZE, ""); + + if (!m_scratchBits) { + this->_wrapped.template readBuffer(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) + readBits(reinterpret_cast(*it), details::BitsSize::value); + } + } + + template + void readBits(T &v, size_t bitsCount) { + static_assert(std::is_integral() && std::is_unsigned(), ""); + readBitsInternal(v, bitsCount); + } + + void align() { + if (m_scratchBits) { + ScratchType tmp{}; + readBitsInternal(tmp, m_scratchBits); + handleAlignErrors(tmp, std::integral_constant{}); + } + } + + void currentReadPos(size_t pos) { + align(); + this->_wrapped.currentReadPos(pos); + } + + size_t currentReadPos() const { + return this->_wrapped.currentReadPos(); + } + + void currentReadEndPos(size_t pos) { + this->_wrapped.currentReadEndPos(pos); + } + + size_t currentReadEndPos() const { + return this->_wrapped.currentReadEndPos(); + } + + bool isCompletedSuccessfully() const { + return this->_wrapped.isCompletedSuccessfully(); + } + + ReaderError error() const { + return this->_wrapped.error(); + } + + void error(ReaderError error) { + this->_wrapped.error(error); + } + + private: + TAdapter& _wrapped; + using UnsignedValue = typename std::make_unsigned::type; + using ScratchType = typename details::ScratchType::type; + + ScratchType m_scratch{}; + size_t m_scratchBits{}; + + template + void readBitsInternal(T &v, size_t size) { + auto bitsLeft = size; + using TFast = typename FastType::type; + TFast res{}; + while (bitsLeft > 0) { + auto bits = (std::min)(bitsLeft, details::BitsSize::value); + if (m_scratchBits < bits) { + UnsignedValue tmp; + this->_wrapped.template readBytes(tmp); + m_scratch |= static_cast(tmp) << m_scratchBits; + m_scratchBits += details::BitsSize::value; + } + auto shiftedRes = + static_cast(m_scratch & ((static_cast(1) << bits) - 1)) << (size - bitsLeft); + res = static_cast(res | static_cast(shiftedRes)); + m_scratch >>= bits; + m_scratchBits -= bits; + bitsLeft -= bits; + } + v = static_cast(res); + } + + void handleAlignErrors(ScratchType value, std::true_type) { + if (value) + error(ReaderError::InvalidData); + } + + void handleAlignErrors(ScratchType, std::false_type) { + } + + }; + + + template + class BasicMeasureSizeBitPackingWrapper { + public: + using BitPackingEnabled = BasicMeasureSizeBitPackingWrapper; + using TConfig = typename TAdapter::TConfig; + using TValue = typename TAdapter::TValue; + + BasicMeasureSizeBitPackingWrapper(TAdapter& adapter) + : _wrapped{adapter} + { + } + + ~BasicMeasureSizeBitPackingWrapper() { + align(); + } + + template + void writeBytes(const T& value) { + _wrapped.template writeBytes(value); + } + + template + void writeBuffer(const T* buf, size_t count) { + _wrapped.template writeBuffer(buf, count); + } + + template + void writeBits(const T& , size_t bitsCount) { + _scratchBits += bitsCount; + while (_scratchBits >= 8) { + writeOneByte(); + _scratchBits -= 8; + } + } + + void align() { + if (_scratchBits > 0) { + _scratchBits = 0; + writeOneByte(); + } + } + + void currentWritePos(size_t pos) { + align(); + this->_wrapped.currentWritePos(pos); + } + + size_t currentWritePos() const { + return this->_wrapped.currentWritePos(); + } + + void flush() { + align(); + this->_wrapped.flush(); + } + + size_t writtenBytesCount() const { + return this->_wrapped.writtenBytesCount(); + } + + private: + void writeOneByte() { + _wrapped.template writeBytes<1>(uint8_t{}); + } + TAdapter& _wrapped; + size_t _scratchBits{}; + }; + + + template + class OutputAdapterBitPackingWrapper { + public: + // in order to check if adapter is BP enabled, we use `std::is_same` + // so when current implementation is BP enabled, we always specify current class as BitPackingEnabled. + using BitPackingEnabled = OutputAdapterBitPackingWrapper; + using TConfig = typename TAdapter::TConfig; + using TValue = typename TAdapter::TValue; + + OutputAdapterBitPackingWrapper(TAdapter& adapter) + : _wrapped{adapter} + { + } + + ~OutputAdapterBitPackingWrapper() { + align(); + } + + template + void writeBytes(const T &v) { + static_assert(std::is_integral(), ""); + static_assert(sizeof(T) == SIZE, ""); + + if (!_scratchBits) { + this->_wrapped.template writeBytes(v); + } else { + using UT = typename std::make_unsigned::type; + writeBitsInternal(reinterpret_cast(v), details::BitsSize::value); + } + } + + template + void writeBuffer(const T *buf, size_t count) { + static_assert(std::is_integral(), ""); + static_assert(sizeof(T) == SIZE, ""); + if (!_scratchBits) { + this->_wrapped.template writeBuffer(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) + writeBitsInternal(reinterpret_cast(*it), details::BitsSize::value); + } + } + + template + void writeBits(const T &v, size_t bitsCount) { + static_assert(std::is_integral() && std::is_unsigned(), ""); + assert(0 < bitsCount && bitsCount <= details::BitsSize::value); + assert(v <= (bitsCount < 64 + ? (1ULL << bitsCount) - 1 + : (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1))); + writeBitsInternal(v, bitsCount); + } + + void align() { + writeBitsInternal(UnsignedType{}, (details::BitsSize::value - _scratchBits) % 8); + } + + void currentWritePos(size_t pos) { + align(); + this->_wrapped.currentWritePos(pos); + } + + size_t currentWritePos() const { + return this->_wrapped.currentWritePos(); + } + + void flush() { + align(); + this->_wrapped.flush(); + } + + size_t writtenBytesCount() const { + return this->_wrapped.writtenBytesCount(); + } + + private: + TAdapter& _wrapped; + + using UnsignedType = typename std::make_unsigned::type; + using ScratchType = typename details::ScratchType::type; + static_assert(details::IsDefined::value, "Underlying adapter value type is not supported"); + + + template + void writeBitsInternal(const T &v, size_t size) { + constexpr size_t valueSize = details::BitsSize::value; + T value = v; + size_t bitsLeft = size; + while (bitsLeft > 0) { + auto bits = (std::min)(bitsLeft, valueSize); + _scratch |= static_cast( value ) << _scratchBits; + _scratchBits += bits; + if (_scratchBits >= valueSize) { + auto tmp = static_cast(_scratch & _MASK); + this->_wrapped.template writeBytes(tmp); + _scratch >>= valueSize; + _scratchBits -= valueSize; + + value = static_cast(value >> valueSize); + } + bitsLeft -= bits; + } + } + + //overload for TValue, for better performance + void writeBitsInternal(const UnsignedType &v, size_t size) { + if (size > 0) { + _scratch |= static_cast( v ) << _scratchBits; + _scratchBits += size; + if (_scratchBits >= details::BitsSize::value) { + auto tmp = static_cast(_scratch & _MASK); + this->_wrapped.template writeBytes(tmp); + _scratch >>= details::BitsSize::value; + _scratchBits -= details::BitsSize::value; + } + } + } + + const UnsignedType _MASK = (std::numeric_limits::max)(); + ScratchType _scratch{}; + size_t _scratchBits{}; + }; + } +} + + +#endif //BITSERY_DETAILS_ADAPTER_BIT_PACKING_H diff --git a/include/bitsery/details/adapter_common.h b/include/bitsery/details/adapter_common.h index d3d0bba..5e06649 100644 --- a/include/bitsery/details/adapter_common.h +++ b/include/bitsery/details/adapter_common.h @@ -23,16 +23,11 @@ #ifndef BITSERY_DETAILS_ADAPTER_COMMON_H #define BITSERY_DETAILS_ADAPTER_COMMON_H -#include -#include -#include -#include -#include -#include -#include #include "not_defined_type.h" - #include "../common.h" +#include +#include +#include namespace bitsery { @@ -231,7 +226,6 @@ namespace bitsery { using type = int_fast64_t; }; - /** * output/input adapter base that handles endianness */ @@ -239,8 +233,6 @@ namespace bitsery { template struct OutputAdapterBaseCRTP { - static constexpr bool BitPackingEnabled = false; - template void writeBytes(const T &v) { static_assert(std::is_integral(), ""); @@ -299,25 +291,24 @@ namespace bitsery { }; - template - struct InputAdapterBaseCRTP { - static constexpr bool BitPackingEnabled = false; + template + struct InputAdapterBaseCRTP { template void readBytes(T& v) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - static_cast(this)->template readInternalValue(reinterpret_cast(&v)); - swapDataBits(v, ShouldSwap{}); + static_cast(this)->template readInternalValue(reinterpret_cast(&v)); + swapDataBits(v, ShouldSwap{}); } template void readBuffer(T* buf, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - static_cast(this)->readInternalBuffer(reinterpret_cast(buf), sizeof(T) * count); - swapDataBits(buf, count, ShouldSwap{}); + static_cast(this)->readInternalBuffer(reinterpret_cast(buf), sizeof(T) * count); + swapDataBits(buf, count, ShouldSwap{}); } template @@ -361,10 +352,7 @@ namespace bitsery { void swapDataBits(T &, std::false_type) { //empty function because no swap is required } - - }; - } } diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h index 17503c6..6edc288 100644 --- a/include/bitsery/details/serialization_common.h +++ b/include/bitsery/details/serialization_common.h @@ -23,12 +23,9 @@ #ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H #define BITSERY_DETAILS_SERIALIZATION_COMMON_H -#include -#include -#include #include "adapter_common.h" #include "../traits/core/traits.h" - +#include namespace bitsery { diff --git a/include/bitsery/ext/compact_value.h b/include/bitsery/ext/compact_value.h index 4a1fa01..34baee2 100644 --- a/include/bitsery/ext/compact_value.h +++ b/include/bitsery/ext/compact_value.h @@ -24,8 +24,6 @@ #define BITSERY_EXT_COMPACT_VALUE_H #include "../details/serialization_common.h" -#include "../details/adapter_common.h" -#include namespace bitsery { diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h index cbe0c48..803e244 100644 --- a/include/bitsery/ext/inheritance.h +++ b/include/bitsery/ext/inheritance.h @@ -23,9 +23,9 @@ #ifndef BITSERY_EXT_INHERITANCE_H #define BITSERY_EXT_INHERITANCE_H -#include #include "../traits/core/traits.h" #include "../ext/utils/memory_resource.h" +#include namespace bitsery { diff --git a/include/bitsery/ext/pointer.h b/include/bitsery/ext/pointer.h index 086f044..16cd3ce 100644 --- a/include/bitsery/ext/pointer.h +++ b/include/bitsery/ext/pointer.h @@ -23,11 +23,11 @@ #ifndef BITSERY_EXT_POINTER_H #define BITSERY_EXT_POINTER_H -#include #include "../traits/core/traits.h" #include "utils/pointer_utils.h" #include "utils/polymorphism_utils.h" #include "utils/rtti_utils.h" +#include namespace bitsery { diff --git a/include/bitsery/ext/std_bitset.h b/include/bitsery/ext/std_bitset.h index b2a6b9e..abd395d 100644 --- a/include/bitsery/ext/std_bitset.h +++ b/include/bitsery/ext/std_bitset.h @@ -59,7 +59,7 @@ namespace bitsery { } } if (LEFTOVER > 0) { - serializeLeftover(ser.adapter(), obj, N - LEFTOVER, N); + serializeLeftoverImpl(ser.adapter(), obj, N - LEFTOVER, N, std::is_same{}); } } @@ -81,15 +81,11 @@ namespace bitsery { obj[offset + 7] = data & 0x80u; } if (LEFTOVER > 0) { - deserializeLeftover(des.adapter(), obj, N - LEFTOVER, N); + deserializeLeftoverImpl(des.adapter(), obj, N - LEFTOVER, N, std::is_same{}); } } private: - template - void serializeLeftover(Writer& w, const std::bitset &obj, size_t from, size_t to) const { - serializeLeftoverImpl(w, obj, from, to, std::integral_constant {}); - } template void serializeLeftoverImpl(Writer& w, const std::bitset &obj, size_t from, size_t to, std::integral_constant) const { @@ -107,11 +103,6 @@ namespace bitsery { } } - template - void deserializeLeftover(Reader& r, std::bitset &obj, size_t from, size_t to) const { - deserializeLeftoverImpl(r, obj, from, to, std::integral_constant {}); - } - template void deserializeLeftoverImpl(Reader& r, std::bitset &obj, size_t from, size_t to, std::integral_constant) const { uint8_t data = 0u; diff --git a/include/bitsery/ext/std_map.h b/include/bitsery/ext/std_map.h index 9cc430c..702b6a9 100644 --- a/include/bitsery/ext/std_map.h +++ b/include/bitsery/ext/std_map.h @@ -24,7 +24,6 @@ #define BITSERY_EXT_STD_MAP_H #include "../traits/core/traits.h" -#include "../details/adapter_common.h" #include "../details/serialization_common.h" //we need this, so we could reserve for non ordered map #include diff --git a/include/bitsery/ext/std_queue.h b/include/bitsery/ext/std_queue.h index 3f935fa..08d39f4 100644 --- a/include/bitsery/ext/std_queue.h +++ b/include/bitsery/ext/std_queue.h @@ -24,11 +24,11 @@ #ifndef BITSERY_EXT_STD_QUEUE_H #define BITSERY_EXT_STD_QUEUE_H -#include -#include //include type traits for deque and vector, because they are defaults for queue and priority_queue #include "../traits/deque.h" #include "../traits/vector.h" +#include +#include namespace bitsery { namespace ext { diff --git a/include/bitsery/ext/std_set.h b/include/bitsery/ext/std_set.h index e2ba134..607ecce 100644 --- a/include/bitsery/ext/std_set.h +++ b/include/bitsery/ext/std_set.h @@ -23,10 +23,7 @@ #ifndef BITSERY_EXT_STD_SET_H #define BITSERY_EXT_STD_SET_H -#include -#include "../details/adapter_common.h" #include "../details/serialization_common.h" -//we need this, so we could reserve for non ordered set #include namespace bitsery { diff --git a/include/bitsery/ext/std_smart_ptr.h b/include/bitsery/ext/std_smart_ptr.h index 14485b2..85872d8 100644 --- a/include/bitsery/ext/std_smart_ptr.h +++ b/include/bitsery/ext/std_smart_ptr.h @@ -23,7 +23,6 @@ #ifndef BITSERY_EXT_STD_SMART_PTR_H #define BITSERY_EXT_STD_SMART_PTR_H -#include #include "../traits/core/traits.h" #include "utils/pointer_utils.h" #include "utils/polymorphism_utils.h" diff --git a/include/bitsery/ext/std_stack.h b/include/bitsery/ext/std_stack.h index 6feba94..4998a7f 100644 --- a/include/bitsery/ext/std_stack.h +++ b/include/bitsery/ext/std_stack.h @@ -24,10 +24,8 @@ #ifndef BITSERY_EXT_STD_STACK_H #define BITSERY_EXT_STD_STACK_H -#include -#include -//include type traits for deque, because stack default underlying container is deque #include "../traits/deque.h" +#include namespace bitsery { namespace ext { diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h index 63678b4..3d4048e 100644 --- a/include/bitsery/ext/utils/pointer_utils.h +++ b/include/bitsery/ext/utils/pointer_utils.h @@ -23,14 +23,7 @@ #ifndef BITSERY_POINTER_UTILS_H #define BITSERY_POINTER_UTILS_H -#include -#include -#include -#include -#include #include "polymorphism_utils.h" -#include "../../details/adapter_common.h" -#include "../../details/serialization_common.h" namespace bitsery { namespace ext { diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h index 6678230..fba4534 100644 --- a/include/bitsery/ext/utils/polymorphism_utils.h +++ b/include/bitsery/ext/utils/polymorphism_utils.h @@ -23,11 +23,10 @@ #ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H #define BITSERY_EXT_POLYMORPHISM_UTILS_H -#include -#include #include "memory_resource.h" -#include "../../details/adapter_common.h" -#include "../../details/serialization_common.h" +#include +#include +#include namespace bitsery { diff --git a/include/bitsery/ext/utils/rtti_utils.h b/include/bitsery/ext/utils/rtti_utils.h index 59f72cf..be9ea5e 100644 --- a/include/bitsery/ext/utils/rtti_utils.h +++ b/include/bitsery/ext/utils/rtti_utils.h @@ -32,10 +32,6 @@ namespace bitsery { struct StandardRTTI { -// static_assert(!std::is_pointer::value && -// !std::is_const::value && -// !std::is_volatile::value, ""); - template static size_t get(TBase& obj) { return typeid(obj).hash_code(); diff --git a/include/bitsery/ext/value_range.h b/include/bitsery/ext/value_range.h index 7894ad7..88ec2fc 100644 --- a/include/bitsery/ext/value_range.h +++ b/include/bitsery/ext/value_range.h @@ -24,8 +24,6 @@ #define BITSERY_EXT_VALUE_RANGE_H #include "../details/serialization_common.h" -#include "../details/adapter_common.h" -#include namespace bitsery { diff --git a/include/bitsery/serializer.h b/include/bitsery/serializer.h index 7203f5e..8e71f9b 100644 --- a/include/bitsery/serializer.h +++ b/include/bitsery/serializer.h @@ -25,148 +25,15 @@ #define BITSERY_SERIALIZER_H #include "details/serialization_common.h" -#include "details/adapter_common.h" #include -#include namespace bitsery { - namespace details { - template - class OutputAdapterBitPackingWrapper { - public: - - static constexpr bool BitPackingEnabled = true; - using TConfig = typename TAdapter::TConfig; - using TValue = typename TAdapter::TValue; - - OutputAdapterBitPackingWrapper(TAdapter& adapter) - : _wrapped{adapter} - { - } - - - ~OutputAdapterBitPackingWrapper() { - align(); - } - - template - void writeBytes(const T &v) { - static_assert(std::is_integral(), ""); - static_assert(sizeof(T) == SIZE, ""); - - if (!_scratchBits) { - this->_wrapped.template writeBytes(v); - } else { - using UT = typename std::make_unsigned::type; - writeBitsInternal(reinterpret_cast(v), details::BitsSize::value); - } - } - - template - void writeBuffer(const T *buf, size_t count) { - static_assert(std::is_integral(), ""); - static_assert(sizeof(T) == SIZE, ""); - if (!_scratchBits) { - this->_wrapped.template writeBuffer(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) - writeBitsInternal(reinterpret_cast(*it), details::BitsSize::value); - } - } - - template - void writeBits(const T &v, size_t bitsCount) { - static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(0 < bitsCount && bitsCount <= details::BitsSize::value); - assert(v <= (bitsCount < 64 - ? (1ULL << bitsCount) - 1 - : (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1))); - writeBitsInternal(v, bitsCount); - } - - void align() { - writeBitsInternal(UnsignedType{}, (details::BitsSize::value - _scratchBits) % 8); - } - - void currentWritePos(size_t pos) { - align(); - this->_wrapped.currentWritePos(pos); - } - - size_t currentWritePos() const { - return this->_wrapped.currentWritePos(); - } - - void flush() { - align(); - this->_wrapped.flush(); - } - - size_t writtenBytesCount() const { - return this->_wrapped.writtenBytesCount(); - } - - private: - TAdapter& _wrapped; - - using UnsignedType = typename std::make_unsigned::type; - using ScratchType = typename details::ScratchType::type; - static_assert(details::IsDefined::value, "Underlying adapter value type is not supported"); - - - template - void writeBitsInternal(const T &v, size_t size) { - constexpr size_t valueSize = details::BitsSize::value; - T value = v; - size_t bitsLeft = size; - while (bitsLeft > 0) { - auto bits = (std::min)(bitsLeft, valueSize); - _scratch |= static_cast( value ) << _scratchBits; - _scratchBits += bits; - if (_scratchBits >= valueSize) { - auto tmp = static_cast(_scratch & _MASK); - this->_wrapped.template writeBytes(tmp); - _scratch >>= valueSize; - _scratchBits -= valueSize; - - value = static_cast(value >> valueSize); - } - bitsLeft -= bits; - } - } - - //overload for TValue, for better performance - void writeBitsInternal(const UnsignedType &v, size_t size) { - if (size > 0) { - _scratch |= static_cast( v ) << _scratchBits; - _scratchBits += size; - if (_scratchBits >= details::BitsSize::value) { - auto tmp = static_cast(_scratch & _MASK); - this->_wrapped.template writeBytes(tmp); - _scratch >>= details::BitsSize::value; - _scratchBits -= details::BitsSize::value; - } - } - } - - const UnsignedType _MASK = (std::numeric_limits::max)(); - ScratchType _scratch{}; - size_t _scratchBits{}; - }; - - } - template class Serializer: public details::AdapterAndContextRef { public: //helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking - using BPEnabledType = Serializer>::type, TContext>; + using BPEnabledType = Serializer; using TConfig = typename TOutputAdapter::TConfig; using details::AdapterAndContextRef::AdapterAndContextRef; @@ -211,7 +78,7 @@ namespace bitsery { template void enableBitPacking(Fnc&& fnc) { procEnableBitPacking(std::forward(fnc), - std::integral_constant{}, + std::is_same{}, std::integral_constant{}); } @@ -252,7 +119,7 @@ namespace bitsery { */ void boolValue(bool v) { - procBoolValue(v, std::integral_constant{}); + procBoolValue(v, std::is_same{}); } /* @@ -501,14 +368,12 @@ namespace bitsery { template void procEnableBitPacking(const Fnc& fnc, std::false_type, std::true_type) { - //create serializer using bitpacking wrapper BPEnabledType ser{this->_context, this->_adapter}; fnc(ser); } template void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type) { - //create serializer using bitpacking wrapper BPEnabledType ser{this->_adapter}; fnc(ser); } diff --git a/include/bitsery/traits/core/std_defaults.h b/include/bitsery/traits/core/std_defaults.h index 0046bd3..564061b 100644 --- a/include/bitsery/traits/core/std_defaults.h +++ b/include/bitsery/traits/core/std_defaults.h @@ -88,7 +88,7 @@ namespace bitsery { using TValue = typename ContainerTraits::TValue; - static void increaseBufferSize(T& container, size_t currSize, size_t minSize) { + static void increaseBufferSize(T& container, size_t /*currSize*/, size_t minSize) { //since we're writing to buffer use different resize strategy than default implementation //when small size grow faster, to avoid thouse 2/4/8/16... byte allocations auto newSize = static_cast(static_cast(container.size()) * 1.5) + 128; diff --git a/include/bitsery/traits/core/traits.h b/include/bitsery/traits/core/traits.h index cfb99dc..f6fbcbe 100644 --- a/include/bitsery/traits/core/traits.h +++ b/include/bitsery/traits/core/traits.h @@ -152,7 +152,7 @@ namespace bitsery { //it is used to dramaticaly improve performance by updating buffer directly //instead of using back_insert_iterator to append each byte to buffer. - static void increaseBufferSize(T& ,size_t currentOffset, size_t minSize) { + static void increaseBufferSize(T& ,size_t , size_t ) { static_assert(std::is_void::value, "Define BufferAdapterTraits or include from to use as buffer adapter container"); } diff --git a/scripts/build.bitsery.cmake b/scripts/build.bitsery.cmake index 236005d..35756b1 100644 --- a/scripts/build.bitsery.cmake +++ b/scripts/build.bitsery.cmake @@ -14,6 +14,6 @@ configure_file(CTestConfig.cmake ${CTEST_SOURCE_DIRECTORY}/CTestConfig.cmake) ctest_start("Continuous") ctest_configure(OPTIONS "-DBITSERY_BUILD_EXAMPLES=OFF;-DBITSERY_BUILD_TESTS=ON") ctest_build() -ctest_test(BUILD ${CTEST_BINARY_DIRECTORY}/tests) +ctest_test() ctest_coverage() #ctest_submit() diff --git a/scripts/show_coverage.sh b/scripts/show_coverage.sh old mode 100644 new mode 100755 diff --git a/tests/adapter.cpp b/tests/adapter.cpp index f7c4192..d9d58d3 100644 --- a/tests/adapter.cpp +++ b/tests/adapter.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include @@ -526,27 +528,34 @@ TYPED_TEST(OutputStreamBuffered, WhenBufferIsStackAllocatedThenBufferSizeViaCtor EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream)); } -TEST(AdapterWriterMeasureSize, CorrectlyMeasuresWrittenBytesCountForSerialization) { - bitsery::MeasureSize w{}; - EXPECT_THAT(w.writtenBytesCount(), Eq(0)); - w.writeBytes<8>(uint64_t{0}); - EXPECT_THAT(w.writtenBytesCount(), Eq(8)); - w.writeBuffer<8, uint64_t>(nullptr, 9); - EXPECT_THAT(w.writtenBytesCount(), Eq(80)); - w.currentWritePos(10); - w.writeBytes<4>(uint32_t{0}); - EXPECT_THAT(w.writtenBytesCount(), Eq(80)); - EXPECT_THAT(w.currentWritePos(), Eq(14)); - w.currentWritePos(80); - EXPECT_THAT(w.writtenBytesCount(), Eq(80)); - w.writeBits(uint32_t{0}, 7u); - EXPECT_THAT(w.writtenBytesCount(), Eq(80)); - w.align(); - EXPECT_THAT(w.writtenBytesCount(), Eq(81)); - w.writeBits(uint32_t{0}, 7u); - w.flush(); - EXPECT_THAT(w.writtenBytesCount(), Eq(82)); - // doesn't compile on older compilers if I write bitsery::MeasureSize::BitPackingEnabled directly in EXPECT_THAT macro. - constexpr bool bpEnabled = bitsery::MeasureSize::BitPackingEnabled; - EXPECT_THAT(bpEnabled, Eq(true)); -} \ No newline at end of file +struct TestData +{ + uint32_t b4; + std::vector vb2; +}; + +template +void serialize(S &s, TestData &o) +{ + s.value4b(o.b4); + s.enableBitPacking([&o](typename S::BPEnabledType &sbp) { + sbp.ext(o.b4, bitsery::ext::ValueRange{0,1023}); // 10 bits + sbp.value4b(o.b4); + sbp.container(o.vb2, 10, [](typename S::BPEnabledType& sbp, uint16_t& data) { + sbp.ext(data, bitsery::ext::ValueRange{0, 200}); // 7 bits + }); + }); + s.container2b(o.vb2, 10); +} + + +TEST(AdapterWriterMeasureSize, CorrectlyMeasuresBytesAndBitsSize) +{ + TestData data{456, {45, 98, 189, 4}}; + + Buffer buf{}; + auto measuredSize = bitsery::quickSerialization(bitsery::MeasureSize{}, data); + auto writtenSize = bitsery::quickSerialization(OutputAdapter{buf}, data); + EXPECT_THAT(measuredSize, Eq(24)); + EXPECT_THAT(measuredSize, Eq(writtenSize)); +}