reworked bitpacking, to fix measure size (measure size now is not

bit-packing enabled by default)
This commit is contained in:
Mindaugas Vinkelis
2022-04-23 21:16:03 +03:00
parent d3dd64baaf
commit d690908541
37 changed files with 474 additions and 486 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.idea/
.vs/
.vscode/
build/
cmake-build-*
CTestConfig.cmake

View File

@@ -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

View File

@@ -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 <algorithm>
#include <cassert>
#include <cstring>
namespace bitsery {
@@ -32,6 +35,8 @@ namespace bitsery {
class InputBufferAdapter: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
public:
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>>;
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<InputBufferAdapter<Buffer, Config>>;
using TConfig = Config;
using TIterator = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TConstIterator;
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
@@ -108,21 +113,21 @@ namespace bitsery {
template <size_t SIZE>
void readInternalValue(TValue *data) {
readInternalChecked(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
readInternalImpl(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalBuffer(TValue *data, size_t size) {
readInternalChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
readInternalImpl(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
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<diff_t>(_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<diff_t>(_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<OutputBufferAdapter<Buffer,Config>> {
public:
friend details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<OutputBufferAdapter<Buffer, Config>>;
using TConfig = Config;
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;

View File

@@ -23,9 +23,9 @@
#ifndef BITSERY_ADAPTER_MEASURE_SIZE_H
#define BITSERY_ADAPTER_MEASURE_SIZE_H
#include "../details/adapter_bit_packing.h"
#include <cstddef>
#include <type_traits>
#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<BasicMeasureSize<Config>>;
using TConfig = Config;
using TValue = void;
@@ -42,53 +42,42 @@ namespace bitsery {
void writeBytes(const T&) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPosBits += details::BitsSize<T>::value;
_currPos += SIZE;
}
template<size_t SIZE, typename T>
void writeBuffer(const T*, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPosBits += details::BitsSize<T>::value * count;
}
template<typename T>
void writeBits(const T&, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(bitsCount <= details::BitsSize<T>::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

View File

@@ -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 <algorithm>
#include <cassert>
#include <ios>
#include <limits>
@@ -34,6 +36,8 @@ namespace bitsery {
class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::InputAdapterBitPackingWrapper<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
@@ -117,6 +121,8 @@ namespace bitsery {
class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
@@ -161,6 +167,8 @@ namespace bitsery {
public details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>> {
public:
friend details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using BitPackingEnabled = details::OutputAdapterBitPackingWrapper<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using TConfig = Config;
using Buffer = TBuffer;
using BufferIt = typename traits::BufferAdapterTraits<TBuffer>::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<std::streamsize>(size));
}
}

View File

@@ -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 <map>
#include <limits>
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename Key, typename T, typename Compare, typename Allocator>

View File

@@ -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 <set>
#include <limits>
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename Key, typename Compare, typename Allocator>

View File

@@ -24,8 +24,8 @@
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
#include <limits>
#include "../ext/std_stack.h"
#include <limits>
namespace bitsery {
template<typename S, typename T, typename C>

View File

@@ -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 <unordered_map>
#include <limits>
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>

View File

@@ -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 <limits>
#include <unordered_set>
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>

View File

@@ -24,8 +24,6 @@
#ifndef BITSERY_COMMON_H
#define BITSERY_COMMON_H
#include <tuple>
namespace bitsery {
/*

View File

@@ -25,150 +25,14 @@
#define BITSERY_DESERIALIZER_H
#include "details/serialization_common.h"
#include "details/adapter_common.h"
#include <utility>
namespace bitsery {
namespace details {
template<typename TAdapter>
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<size_t SIZE, typename T>
void readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
if (!m_scratchBits)
this->_wrapped.template readBytes<SIZE,T>(v);
else
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
}
template<size_t SIZE, typename T>
void readBuffer(T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
readBitsInternal(v, bitsCount);
}
void align() {
if (m_scratchBits) {
ScratchType tmp{};
readBitsInternal(tmp, m_scratchBits);
handleAlignErrors(tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
}
}
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<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
ScratchType m_scratch{};
size_t m_scratchBits{};
template<typename T>
void readBitsInternal(T &v, size_t size) {
auto bitsLeft = size;
using TFast = typename FastType<T>::type;
TFast res{};
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
UnsignedValue tmp;
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
m_scratchBits += details::BitsSize<UnsignedValue>::value;
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
m_scratch >>= bits;
m_scratchBits -= bits;
bitsLeft -= bits;
}
v = static_cast<T>(res);
}
void handleAlignErrors(ScratchType value, std::true_type) {
if (value)
error(ReaderError::InvalidData);
}
void handleAlignErrors(ScratchType, std::false_type) {
}
};
}
template<typename TInputAdapter, typename TContext = void>
class Deserializer: public details::AdapterAndContextRef<TInputAdapter, TContext> {
public:
//helper type, that always returns bit-packing enabled type, useful inside deserialize function when enabling bitpacking
using BPEnabledType = Deserializer<typename std::conditional<TInputAdapter::BitPackingEnabled,
TInputAdapter,
details::InputAdapterBitPackingWrapper<TInputAdapter>>::type, TContext>;
using BPEnabledType = Deserializer<typename TInputAdapter::BitPackingEnabled, TContext>;
using TConfig = typename TInputAdapter::TConfig;
using details::AdapterAndContextRef<TInputAdapter, TContext>::AdapterAndContextRef;
@@ -213,7 +77,7 @@ namespace bitsery {
*/
template <typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{});
procEnableBitPacking(std::forward<Fnc>(fnc), std::is_same<TInputAdapter, typename TInputAdapter::BitPackingEnabled>{});
}
/*
@@ -253,7 +117,7 @@ namespace bitsery {
*/
void boolValue(bool &v) {
procBoolValue(v,
std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{},
std::is_same<TInputAdapter, typename TInputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
}
@@ -518,7 +382,6 @@ namespace bitsery {
template <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
//create deserializer using bitpacking wrapper
auto des = createWithContext(std::integral_constant<bool, Deserializer::HasContext>{});
fnc(des);
}

View File

@@ -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 <limits>
namespace bitsery {
namespace details {
template<typename TAdapter>
class InputAdapterBitPackingWrapper {
public:
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter, typename Adapter::BitPackingEnabled>`
// so when current implementation is BP enabled, we always specify current class as BitPackingEnabled.
using BitPackingEnabled = InputAdapterBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
InputAdapterBitPackingWrapper(TAdapter& adapter)
: _wrapped{adapter}
{
}
~InputAdapterBitPackingWrapper() {
align();
}
template<size_t SIZE, typename T>
void readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
if (!m_scratchBits)
this->_wrapped.template readBytes<SIZE,T>(v);
else
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
}
template<size_t SIZE, typename T>
void readBuffer(T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
readBitsInternal(v, bitsCount);
}
void align() {
if (m_scratchBits) {
ScratchType tmp{};
readBitsInternal(tmp, m_scratchBits);
handleAlignErrors(tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
}
}
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<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
ScratchType m_scratch{};
size_t m_scratchBits{};
template<typename T>
void readBitsInternal(T &v, size_t size) {
auto bitsLeft = size;
using TFast = typename FastType<T>::type;
TFast res{};
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
UnsignedValue tmp;
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
m_scratchBits += details::BitsSize<UnsignedValue>::value;
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
m_scratch >>= bits;
m_scratchBits -= bits;
bitsLeft -= bits;
}
v = static_cast<T>(res);
}
void handleAlignErrors(ScratchType value, std::true_type) {
if (value)
error(ReaderError::InvalidData);
}
void handleAlignErrors(ScratchType, std::false_type) {
}
};
template <typename TAdapter>
class BasicMeasureSizeBitPackingWrapper {
public:
using BitPackingEnabled = BasicMeasureSizeBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
BasicMeasureSizeBitPackingWrapper(TAdapter& adapter)
: _wrapped{adapter}
{
}
~BasicMeasureSizeBitPackingWrapper() {
align();
}
template<size_t SIZE, typename T>
void writeBytes(const T& value) {
_wrapped.template writeBytes<SIZE>(value);
}
template<size_t SIZE, typename T>
void writeBuffer(const T* buf, size_t count) {
_wrapped.template writeBuffer<SIZE>(buf, count);
}
template<typename T>
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<typename TAdapter>
class OutputAdapterBitPackingWrapper {
public:
// in order to check if adapter is BP enabled, we use `std::is_same<Adapter, typename Adapter::BitPackingEnabled>`
// so when current implementation is BP enabled, we always specify current class as BitPackingEnabled.
using BitPackingEnabled = OutputAdapterBitPackingWrapper<TAdapter>;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
OutputAdapterBitPackingWrapper(TAdapter& adapter)
: _wrapped{adapter}
{
}
~OutputAdapterBitPackingWrapper() {
align();
}
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBytes<SIZE,T>(v);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
}
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void writeBits(const T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
assert(v <= (bitsCount < 64
? (1ULL << bitsCount) - 1
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
writeBitsInternal(v, bitsCount);
}
void align() {
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::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<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedType>::type;
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
template<typename T>
void writeBitsInternal(const T &v, size_t size) {
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
T value = v;
size_t bitsLeft = size;
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, valueSize);
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
_scratchBits += bits;
if (_scratchBits >= valueSize) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
_scratch >>= valueSize;
_scratchBits -= valueSize;
value = static_cast<T>(value >> valueSize);
}
bitsLeft -= bits;
}
}
//overload for TValue, for better performance
void writeBitsInternal(const UnsignedType &v, size_t size) {
if (size > 0) {
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
_scratchBits += size;
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
_scratch >>= details::BitsSize<UnsignedType>::value;
_scratchBits -= details::BitsSize<UnsignedType>::value;
}
}
}
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
ScratchType _scratch{};
size_t _scratchBits{};
};
}
}
#endif //BITSERY_DETAILS_ADAPTER_BIT_PACKING_H

View File

@@ -23,16 +23,11 @@
#ifndef BITSERY_DETAILS_ADAPTER_COMMON_H
#define BITSERY_DETAILS_ADAPTER_COMMON_H
#include <algorithm>
#include <utility>
#include <cassert>
#include <vector>
#include <stack>
#include <cstring>
#include <climits>
#include "not_defined_type.h"
#include "../common.h"
#include <algorithm>
#include <cassert>
#include <climits>
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<typename Adapter>
struct OutputAdapterBaseCRTP {
static constexpr bool BitPackingEnabled = false;
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
@@ -299,25 +291,24 @@ namespace bitsery {
};
template <typename Base>
struct InputAdapterBaseCRTP {
static constexpr bool BitPackingEnabled = false;
template <typename Adapter>
struct InputAdapterBaseCRTP {
template<size_t SIZE, typename T>
void readBytes(T& v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
static_cast<Base*>(this)->template readInternalValue<sizeof(T)>(reinterpret_cast<typename Base::TValue *>(&v));
swapDataBits(v, ShouldSwap<typename Base::TConfig>{});
static_cast<Adapter*>(this)->template readInternalValue<sizeof(T)>(reinterpret_cast<typename Adapter::TValue *>(&v));
swapDataBits(v, ShouldSwap<typename Adapter::TConfig>{});
}
template<size_t SIZE, typename T>
void readBuffer(T* buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
static_cast<Base*>(this)->readInternalBuffer(reinterpret_cast<typename Base::TValue *>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Base::TConfig>{});
static_cast<Adapter*>(this)->readInternalBuffer(reinterpret_cast<typename Adapter::TValue *>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Adapter::TConfig>{});
}
template<typename T>
@@ -361,10 +352,7 @@ namespace bitsery {
void swapDataBits(T &, std::false_type) {
//empty function because no swap is required
}
};
}
}

View File

@@ -23,12 +23,9 @@
#ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
#include <type_traits>
#include <utility>
#include <tuple>
#include "adapter_common.h"
#include "../traits/core/traits.h"
#include <tuple>
namespace bitsery {

View File

@@ -24,8 +24,6 @@
#define BITSERY_EXT_COMPACT_VALUE_H
#include "../details/serialization_common.h"
#include "../details/adapter_common.h"
#include <cassert>
namespace bitsery {

View File

@@ -23,9 +23,9 @@
#ifndef BITSERY_EXT_INHERITANCE_H
#define BITSERY_EXT_INHERITANCE_H
#include <unordered_set>
#include "../traits/core/traits.h"
#include "../ext/utils/memory_resource.h"
#include <unordered_set>
namespace bitsery {

View File

@@ -23,11 +23,11 @@
#ifndef BITSERY_EXT_POINTER_H
#define BITSERY_EXT_POINTER_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
#include <cassert>
namespace bitsery {

View File

@@ -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<Ser, typename Ser::BPEnabledType>{});
}
}
@@ -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<Des, typename Des::BPEnabledType>{});
}
}
private:
template<typename Writer, size_t N>
void serializeLeftover(Writer& w, const std::bitset<N> &obj, size_t from, size_t to) const {
serializeLeftoverImpl(w, obj, from, to, std::integral_constant<bool, Writer::BitPackingEnabled> {});
}
template<typename Writer, size_t N>
void serializeLeftoverImpl(Writer& w, const std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, false>) const {
@@ -107,11 +103,6 @@ namespace bitsery {
}
}
template<typename Reader, size_t N>
void deserializeLeftover(Reader& r, std::bitset<N> &obj, size_t from, size_t to) const {
deserializeLeftoverImpl(r, obj, from, to, std::integral_constant<bool, Reader::BitPackingEnabled> {});
}
template<typename Reader, size_t N>
void deserializeLeftoverImpl(Reader& r, std::bitset<N> &obj, size_t from, size_t to, std::integral_constant<bool, false>) const {
uint8_t data = 0u;

View File

@@ -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 <unordered_map>

View File

@@ -24,11 +24,11 @@
#ifndef BITSERY_EXT_STD_QUEUE_H
#define BITSERY_EXT_STD_QUEUE_H
#include <type_traits>
#include <queue>
//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 <type_traits>
#include <queue>
namespace bitsery {
namespace ext {

View File

@@ -23,10 +23,7 @@
#ifndef BITSERY_EXT_STD_SET_H
#define BITSERY_EXT_STD_SET_H
#include <cassert>
#include "../details/adapter_common.h"
#include "../details/serialization_common.h"
//we need this, so we could reserve for non ordered set
#include <unordered_set>
namespace bitsery {

View File

@@ -23,7 +23,6 @@
#ifndef BITSERY_EXT_STD_SMART_PTR_H
#define BITSERY_EXT_STD_SMART_PTR_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"

View File

@@ -24,10 +24,8 @@
#ifndef BITSERY_EXT_STD_STACK_H
#define BITSERY_EXT_STD_STACK_H
#include <type_traits>
#include <stack>
//include type traits for deque, because stack default underlying container is deque
#include "../traits/deque.h"
#include <stack>
namespace bitsery {
namespace ext {

View File

@@ -23,14 +23,7 @@
#ifndef BITSERY_POINTER_UTILS_H
#define BITSERY_POINTER_UTILS_H
#include <unordered_map>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>
#include "polymorphism_utils.h"
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {

View File

@@ -23,11 +23,10 @@
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
#include <unordered_map>
#include <memory>
#include "memory_resource.h"
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
#include <memory>
#include <vector>
#include <unordered_map>
namespace bitsery {

View File

@@ -32,10 +32,6 @@ namespace bitsery {
struct StandardRTTI {
// static_assert(!std::is_pointer<TBase>::value &&
// !std::is_const<TBase>::value &&
// !std::is_volatile<TBase>::value, "");
template<typename TBase>
static size_t get(TBase& obj) {
return typeid(obj).hash_code();

View File

@@ -24,8 +24,6 @@
#define BITSERY_EXT_VALUE_RANGE_H
#include "../details/serialization_common.h"
#include "../details/adapter_common.h"
#include <cassert>
namespace bitsery {

View File

@@ -25,148 +25,15 @@
#define BITSERY_SERIALIZER_H
#include "details/serialization_common.h"
#include "details/adapter_common.h"
#include <cassert>
#include <limits>
namespace bitsery {
namespace details {
template<typename TAdapter>
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<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBytes<SIZE,T>(v);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
}
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void writeBits(const T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
assert(v <= (bitsCount < 64
? (1ULL << bitsCount) - 1
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
writeBitsInternal(v, bitsCount);
}
void align() {
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::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<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedType>::type;
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
template<typename T>
void writeBitsInternal(const T &v, size_t size) {
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
T value = v;
size_t bitsLeft = size;
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, valueSize);
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
_scratchBits += bits;
if (_scratchBits >= valueSize) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
_scratch >>= valueSize;
_scratchBits -= valueSize;
value = static_cast<T>(value >> valueSize);
}
bitsLeft -= bits;
}
}
//overload for TValue, for better performance
void writeBitsInternal(const UnsignedType &v, size_t size) {
if (size > 0) {
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
_scratchBits += size;
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
_scratch >>= details::BitsSize<UnsignedType>::value;
_scratchBits -= details::BitsSize<UnsignedType>::value;
}
}
}
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
ScratchType _scratch{};
size_t _scratchBits{};
};
}
template<typename TOutputAdapter, typename TContext = void>
class Serializer: public details::AdapterAndContextRef<TOutputAdapter, TContext> {
public:
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
using BPEnabledType = Serializer<typename std::conditional<TOutputAdapter::BitPackingEnabled,
TOutputAdapter,
details::OutputAdapterBitPackingWrapper<TOutputAdapter>>::type, TContext>;
using BPEnabledType = Serializer<typename TOutputAdapter::BitPackingEnabled, TContext>;
using TConfig = typename TOutputAdapter::TConfig;
using details::AdapterAndContextRef<TOutputAdapter, TContext>::AdapterAndContextRef;
@@ -211,7 +78,7 @@ namespace bitsery {
template <typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc),
std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{},
std::is_same<TOutputAdapter, typename TOutputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, Serializer::HasContext>{});
}
@@ -252,7 +119,7 @@ namespace bitsery {
*/
void boolValue(bool v) {
procBoolValue(v, std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{});
procBoolValue(v, std::is_same<TOutputAdapter, typename TOutputAdapter::BitPackingEnabled>{});
}
/*
@@ -501,14 +368,12 @@ namespace bitsery {
template <typename Fnc>
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 <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type) {
//create serializer using bitpacking wrapper
BPEnabledType ser{this->_adapter};
fnc(ser);
}

View File

@@ -88,7 +88,7 @@ namespace bitsery {
using TValue = typename ContainerTraits<T>::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<size_t>(static_cast<double>(container.size()) * 1.5) + 128;

View File

@@ -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<T>::value,
"Define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer adapter container");
}

View File

@@ -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()

0
scripts/show_coverage.sh Normal file → Executable file
View File

View File

@@ -28,6 +28,8 @@
#include <bitsery/traits/vector.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <bitsery/ext/value_range.h>
#include <bitsery/serializer.h>
#include <gmock/gmock.h>
@@ -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));
}
struct TestData
{
uint32_t b4;
std::vector<uint16_t> vb2;
};
template <typename S>
void serialize(S &s, TestData &o)
{
s.value4b(o.b4);
s.enableBitPacking([&o](typename S::BPEnabledType &sbp) {
sbp.ext(o.b4, bitsery::ext::ValueRange<uint32_t>{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<uint16_t>{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));
}