mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 00:03:54 +00:00
reworked bitpacking, to fix measure size (measure size now is not
bit-packing enabled by default)
This commit is contained in:
4
.github/workflows/on_linux.yml
vendored
4
.github/workflows/on_linux.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/on_mac.yml
vendored
4
.github/workflows/on_mac.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/on_windows.yml
vendored
4
.github/workflows/on_windows.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
build/
|
||||
cmake-build-*
|
||||
CTestConfig.cmake
|
||||
|
||||
70
.travis.yml
70
.travis.yml
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#ifndef BITSERY_COMMON_H
|
||||
#define BITSERY_COMMON_H
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
364
include/bitsery/details/adapter_bit_packing.h
Normal file
364
include/bitsery/details/adapter_bit_packing.h
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#define BITSERY_EXT_COMPACT_VALUE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#define BITSERY_EXT_VALUE_RANGE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
0
scripts/show_coverage.sh
Normal file → Executable 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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user