mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 08:13:56 +00:00
added adapter type for serializer/deserializer
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.idea/
|
||||
.vs/
|
||||
build/
|
||||
cmake-build-debug/
|
||||
|
||||
@@ -13,6 +13,10 @@ ExtensionTraits gain additional patameter BitPackingRequired, static_asserts if
|
||||
Removed boolByte, boolBit, and added boolValue and it writes bit or byte, depeding on if bit-packing is enabled or not.
|
||||
added missing std containers support: forward_list, deque, stack, queue, priority_queue, set, multiset, unordered_set, unordered_multiset
|
||||
Renamed ContainerMap to StdMap, Optional to StdOptional
|
||||
Improved error messages
|
||||
Lots of renaming ...
|
||||
Added adapters for easier extension
|
||||
Config no longer needs typedef *Buffer*
|
||||
|
||||
|
||||
todo write tests:
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
errors handling design
|
||||
usage of NotDefinedType, and how it helps to reduce error stack
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
struct MyStruct {
|
||||
@@ -17,32 +19,22 @@ void serialize(S& s, MyStruct& o) {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
|
||||
//create serializer
|
||||
//1) create buffer to store data
|
||||
//create buffer to store data
|
||||
std::vector<uint8_t> buffer;
|
||||
//2) create buffer writer that is able to write bytes or bits to buffer
|
||||
BufferWriter bw{buffer};
|
||||
//3) create serializer
|
||||
Serializer ser{bw};
|
||||
|
||||
//serialize object, can also be invoked like this: serialize(ser, data)
|
||||
ser.object(data);
|
||||
auto writtenSize = startSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
//flush to buffer, before creating buffer reader
|
||||
bw.flush();
|
||||
auto state = startDeserialization<InputAdapter>(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
|
||||
//create deserializer
|
||||
//1) create buffer reader
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
//2) create deserializer
|
||||
Deserializer des{br};
|
||||
|
||||
//deserialize same object, can also be invoked like this: serialize(des, data)
|
||||
des.object(res);
|
||||
assert(state.first == ReaderError::NO_ERROR && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
#include <bitsery/ext/growable.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
@@ -63,41 +65,25 @@ namespace MyTypes {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer =std::array<uint8_t, 1000000>;
|
||||
//change configuration
|
||||
struct NonDefaultConfig: public bitsery::DefaultConfig {
|
||||
//change underlying buffer
|
||||
using BufferType = Buffer;
|
||||
};
|
||||
using Buffer = std::array<uint8_t, 1000000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
struct SessionsEnabled:public DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
|
||||
//create serializer
|
||||
//1) create buffer to store data
|
||||
Buffer buffer{};
|
||||
//2) create buffer writer that is able to write bytes or bits to buffer
|
||||
BasicBufferWriter<NonDefaultConfig> bw{buffer};
|
||||
//3) create serializer
|
||||
BasicSerializer<NonDefaultConfig, false> ser{bw};
|
||||
|
||||
//serialize object, can also be invoked like this: serialize(ser, data)
|
||||
ser.object(data);
|
||||
|
||||
//flush to buffer, before creating buffer reader, this will always write sessions data for forward/backward compatibility
|
||||
bw.flush();
|
||||
|
||||
//create deserializer
|
||||
//1) create buffer reader
|
||||
BasicBufferReader<NonDefaultConfig> br{bw.getWrittenRange()};
|
||||
//2) create deserializer
|
||||
BasicDeserializer<NonDefaultConfig, false> des{br};
|
||||
auto writtenSize = startSerialization<OutputAdapter, MyTypes::Monster, SessionsEnabled>(buffer, data);
|
||||
|
||||
//deserialize same object, can also be invoked like this: serialize(des, data)
|
||||
MyTypes::Monster res{};
|
||||
des.object(res);
|
||||
|
||||
auto state = startDeserialization<InputAdapter, MyTypes::Monster, SessionsEnabled>(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
assert(state.first == ReaderError::NO_ERROR && state.second);
|
||||
}
|
||||
|
||||
178
include/bitsery/adapters/buffer_adapters.h
Normal file
178
include/bitsery/adapters/buffer_adapters.h
Normal file
@@ -0,0 +1,178 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 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_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
#define BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
|
||||
#include "../details/buffer_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Buffer>
|
||||
class InputBufferAdapter {
|
||||
public:
|
||||
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator end)
|
||||
:_pos{begin},
|
||||
_end{end}
|
||||
{
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
:InputBufferAdapter(begin, std::next(begin, size))
|
||||
{
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = _pos;
|
||||
_pos += size;
|
||||
if (std::distance(_pos, _end) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
_pos -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (getError() == ReaderError::NO_ERROR)
|
||||
setError(ReaderError::DATA_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderError getError() const {
|
||||
auto res = std::distance(_end, _pos);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<ReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return ReaderError::NO_ERROR;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
_end = _pos;
|
||||
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
|
||||
std::advance(_pos, static_cast<size_t>(error));
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _pos == _end;
|
||||
}
|
||||
private:
|
||||
friend details::SessionAccess;
|
||||
|
||||
TIterator _pos;
|
||||
TIterator _end;
|
||||
};
|
||||
|
||||
|
||||
template<typename Container>
|
||||
class OutputBufferAdapter {
|
||||
public:
|
||||
|
||||
using TIterator = typename traits::BufferAdapterTraits<Container>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Container>::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
|
||||
OutputBufferAdapter(Container &buffer)
|
||||
: _buffer{buffer}
|
||||
{
|
||||
|
||||
init(TResizable{});
|
||||
}
|
||||
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
|
||||
size_t getWrittenBytesCount() const {
|
||||
return static_cast<size_t>(std::distance(std::begin(_buffer), _outIt));
|
||||
}
|
||||
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Container>::isResizable>;
|
||||
|
||||
Container &_buffer;
|
||||
TIterator _outIt;
|
||||
TIterator _end;
|
||||
|
||||
/*
|
||||
* resizable buffer
|
||||
*/
|
||||
|
||||
void init(std::true_type) {
|
||||
//resize buffer immediately, because we need output iterator at valid position
|
||||
if (traits::ContainerTraits<Container>::size(_buffer) == 0u) {
|
||||
traits::BufferAdapterTraits<Container>::increaseBufferSize(_buffer);
|
||||
}
|
||||
_end = std::end(_buffer);
|
||||
_outIt = std::begin(_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::true_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
} else {
|
||||
_outIt -= size;
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(_buffer), _outIt);
|
||||
//increase container size
|
||||
traits::BufferAdapterTraits<Container>::increaseBufferSize(_buffer);
|
||||
//restore iterators
|
||||
_end = std::end(_buffer);
|
||||
_outIt = std::next(std::begin(_buffer), pos);
|
||||
|
||||
writeInternal(data, size, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* non resizable buffer
|
||||
*/
|
||||
void init(std::false_type) {
|
||||
_outIt = std::begin(_buffer);
|
||||
_end = std::end(_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
@@ -37,9 +37,6 @@ BITSERY_QUOTE_MACRO(patch)
|
||||
#define BITSERY_VERSION \
|
||||
BITSERY_BUILD_VERSION_STR(BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
|
||||
|
||||
|
||||
#include "buffer_writer.h"
|
||||
#include "buffer_reader.h"
|
||||
#include "serializer.h"
|
||||
#include "deserializer.h"
|
||||
|
||||
|
||||
@@ -25,51 +25,41 @@
|
||||
#ifndef BITSERY_BUFFER_READER_H
|
||||
#define BITSERY_BUFFER_READER_H
|
||||
|
||||
#include "details/buffer_common.h"
|
||||
#include "details/sessions.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Config>
|
||||
template <typename TReader>
|
||||
class BitPackingReader;
|
||||
|
||||
template<typename Config>
|
||||
struct BasicBufferReader {
|
||||
template<typename Config, typename InputAdapter>
|
||||
struct BasicReader {
|
||||
|
||||
using BufferType = typename Config::BufferType;
|
||||
using ValueType = typename details::ContainerTraits<BufferType>::TValue;
|
||||
using BufferIteratorType = typename details::BufferContainerTraits<BufferType>::TIterator;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
using TValue = typename InputAdapter::TValue;
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
|
||||
BasicBufferReader(BufferIteratorType data, BufferIteratorType end)
|
||||
: _bufferContext{data, end},
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit BasicReader(InputAdapter adapter)
|
||||
: _bufferContext{std::move(adapter)},
|
||||
_session{*this, _bufferContext}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
explicit BasicBufferReader(BufferRange<BufferIteratorType> range)
|
||||
:BasicBufferReader(range.begin(), range.end()) {
|
||||
static_assert(std::is_same<
|
||||
typename std::iterator_traits<BufferIteratorType>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
"BufferReader only accepts random access iterators");
|
||||
}
|
||||
BasicReader(const BasicReader &) = delete;
|
||||
|
||||
BasicBufferReader(const BasicBufferReader &) = delete;
|
||||
BasicReader &operator=(const BasicReader &) = delete;
|
||||
|
||||
BasicBufferReader &operator=(const BasicBufferReader &) = delete;
|
||||
BasicReader(BasicReader &&) noexcept = default;
|
||||
|
||||
BasicBufferReader(BasicBufferReader &&) noexcept = default;
|
||||
BasicReader &operator=(BasicReader &&) noexcept = default;
|
||||
|
||||
BasicBufferReader &operator=(BasicBufferReader &&) noexcept = default;
|
||||
|
||||
~BasicBufferReader() noexcept = default;
|
||||
~BasicReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
@@ -86,6 +76,12 @@ namespace bitsery {
|
||||
directRead(buf, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &, size_t ) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Deserializer with bit packing enabled.");
|
||||
}
|
||||
|
||||
void align() {
|
||||
}
|
||||
|
||||
@@ -93,42 +89,42 @@ namespace bitsery {
|
||||
return _bufferContext.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
}
|
||||
|
||||
BufferReaderError getError() const {
|
||||
ReaderError getError() const {
|
||||
auto err = _bufferContext.getError();
|
||||
if (_session.hasActiveSessions() && err == BufferReaderError::BUFFER_OVERFLOW)
|
||||
return BufferReaderError::NO_ERROR;
|
||||
if (_session.hasActiveSessions() && err == ReaderError::DATA_OVERFLOW)
|
||||
return ReaderError::NO_ERROR;
|
||||
return err;
|
||||
}
|
||||
|
||||
void setError(BufferReaderError error) {
|
||||
void setError(ReaderError error) {
|
||||
return _bufferContext.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
if (getError() != BufferReaderError::INVALID_BUFFER_DATA) {
|
||||
if (getError() != ReaderError::INVALID_DATA) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
if (getError() != BufferReaderError::INVALID_BUFFER_DATA) {
|
||||
if (getError() != ReaderError::INVALID_DATA) {
|
||||
_session.end();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BitPackingReader<Config>;
|
||||
friend class BitPackingReader<BasicReader<Config, InputAdapter>>;
|
||||
|
||||
details::ReadBufferContext<BufferType> _bufferContext;
|
||||
InputAdapter _bufferContext;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
details::BufferSessionsReader<BasicBufferReader<Config>, details::ReadBufferContext<BufferType>>,
|
||||
details::DisabledBufferSessionsReader<Config>>::type
|
||||
session::SessionsReader<BasicReader<Config, InputAdapter>>,
|
||||
session::DisabledSessionsReader<BasicReader<Config, InputAdapter>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_bufferContext.read(reinterpret_cast<ValueType *>(v), sizeof(T) * count);
|
||||
_bufferContext.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
@@ -146,19 +142,19 @@ namespace bitsery {
|
||||
|
||||
};
|
||||
|
||||
template<typename Config>
|
||||
template<typename TReader>
|
||||
struct BitPackingReader {
|
||||
|
||||
using ValueType = typename details::ContainerTraits<typename Config::BufferType>::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
using TValue = typename TReader::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
|
||||
explicit BitPackingReader(BasicBufferReader<Config>& reader):_reader{reader}
|
||||
explicit BitPackingReader(TReader& reader):_reader{reader}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<TValue>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
static_assert(sizeof(TValue) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
static_assert(sizeof(TValue) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BitPackingReader(const BitPackingReader&) = delete;
|
||||
@@ -210,7 +206,7 @@ namespace bitsery {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
setError(ReaderError::INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,11 +214,11 @@ namespace bitsery {
|
||||
return _reader.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
BufferReaderError getError() const {
|
||||
ReaderError getError() const {
|
||||
return _reader.getError();
|
||||
}
|
||||
|
||||
void setError(BufferReaderError error) {
|
||||
void setError(ReaderError error) {
|
||||
_reader.setError(error);
|
||||
}
|
||||
|
||||
@@ -237,7 +233,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
private:
|
||||
BasicBufferReader<Config>& _reader;
|
||||
TReader& _reader;
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
@@ -246,12 +242,12 @@ namespace bitsery {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BITS_SIZE<ValueType>::value);
|
||||
auto bits = std::min(bitsLeft, details::BITS_SIZE<TValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
ValueType tmp;
|
||||
_reader.template readBytes<sizeof(ValueType), ValueType>(tmp);
|
||||
TValue tmp;
|
||||
_reader.template readBytes<sizeof(TValue), TValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BITS_SIZE<ValueType>::value;
|
||||
m_scratchBits += details::BITS_SIZE<TValue>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
@@ -264,8 +260,6 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
};
|
||||
//helper type
|
||||
using BufferReader = BasicBufferReader<DefaultConfig>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_READER_H
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
#define BITSERY_BUFFER_WRITER_H
|
||||
|
||||
#include "details/buffer_common.h"
|
||||
#include "details/sessions.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
struct MeasureSize {
|
||||
@@ -63,8 +66,7 @@ namespace bitsery {
|
||||
align();
|
||||
//flush sessions count
|
||||
if (_sessionsBytesCount > 0) {
|
||||
auto sessionsDataSizeBytesCount = (_sessionsBytesCount < 0x8000u ? 2 : 4);
|
||||
_bitsCount += (_sessionsBytesCount + sessionsDataSizeBytesCount) * 8;
|
||||
_bitsCount += (_sessionsBytesCount + 4) * 8;
|
||||
_sessionsBytesCount = 0;
|
||||
}
|
||||
}
|
||||
@@ -93,34 +95,30 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
|
||||
template <typename Config>
|
||||
template <typename TWriter>
|
||||
class BitPackingWriter;
|
||||
|
||||
template<typename Config>
|
||||
struct BasicBufferWriter {
|
||||
using BufferType = typename Config::BufferType;
|
||||
using ValueType = typename details::ContainerTraits<BufferType>::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
using BufferContext = details::WriteBufferContext<BufferType, details::ContainerTraits<BufferType>::isResizable>;
|
||||
template<typename Config, typename OutputAdapter>
|
||||
struct BasicWriter {
|
||||
using TValue = typename OutputAdapter::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit BasicBufferWriter(BufferType &buffer)
|
||||
: _bufferContext{buffer}
|
||||
explicit BasicWriter(OutputAdapter adapter)
|
||||
: _outputAdapter{std::move(adapter)}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferType value type must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BasicBufferWriter(const BasicBufferWriter &) = delete;
|
||||
BasicWriter(const BasicWriter &) = delete;
|
||||
|
||||
BasicBufferWriter &operator=(const BasicBufferWriter &) = delete;
|
||||
BasicWriter &operator=(const BasicWriter &) = delete;
|
||||
|
||||
BasicBufferWriter(BasicBufferWriter &&) noexcept = default;
|
||||
BasicWriter(BasicWriter &&) noexcept = default;
|
||||
|
||||
BasicBufferWriter &operator=(BasicBufferWriter &&) noexcept = default;
|
||||
BasicWriter &operator=(BasicWriter &&) noexcept = default;
|
||||
|
||||
~BasicBufferWriter() noexcept = default;
|
||||
~BasicWriter() noexcept = default;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
@@ -137,6 +135,12 @@ namespace bitsery {
|
||||
directWrite(buf, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t ) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Serializer with bit packing enabled.");
|
||||
}
|
||||
|
||||
//to have the same interface as bitpackingwriter
|
||||
void align() {
|
||||
|
||||
@@ -146,8 +150,8 @@ namespace bitsery {
|
||||
_session.flushSessions(*this);
|
||||
}
|
||||
|
||||
BufferRange<typename details::BufferContainerTraits<BufferType>::TIterator> getWrittenRange() const {
|
||||
return _bufferContext.getWrittenRange();
|
||||
size_t getWrittenBytesCount() const {
|
||||
return _outputAdapter.getWrittenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
@@ -159,7 +163,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BitPackingWriter<Config>;
|
||||
friend class BitPackingWriter<BasicWriter<Config, OutputAdapter>>;
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
@@ -170,34 +174,30 @@ namespace bitsery {
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_bufferContext.write(reinterpret_cast<const ValueType *>(&res), sizeof(T));
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
_bufferContext.write(reinterpret_cast<const ValueType *>(v), count * sizeof(T));
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
BufferContext _bufferContext;
|
||||
OutputAdapter _outputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
details::BufferSessionsWriter<BasicBufferWriter<Config>>,
|
||||
details::DisabledBufferSessionsWriter<Config>>::type
|
||||
session::SessionsWriter<BasicWriter<Config, OutputAdapter>>,
|
||||
session::DisabledSessionsWriter<BasicWriter<Config, OutputAdapter>>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
template<typename Config>
|
||||
template<typename TWriter>
|
||||
struct BitPackingWriter {
|
||||
using ValueType = typename details::ContainerTraits<typename Config::BufferType>::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
using TValue = typename TWriter::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
|
||||
explicit BitPackingWriter(BasicBufferWriter<Config> &writer)
|
||||
explicit BitPackingWriter(TWriter &writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferType value type must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BitPackingWriter(const BitPackingWriter&) = delete;
|
||||
@@ -249,7 +249,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(ValueType{}, (details::BITS_SIZE<ValueType>::value - _scratchBits) % 8);
|
||||
writeBitsInternal(TValue{}, (details::BITS_SIZE<TValue>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
@@ -257,8 +257,8 @@ namespace bitsery {
|
||||
_writer._session.flushSessions(_writer);
|
||||
}
|
||||
|
||||
BufferRange<typename details::BufferContainerTraits<typename Config::BufferType>::TIterator> getWrittenRange() const {
|
||||
return _writer.getWrittenRange();
|
||||
size_t getWrittenBytesCount() const {
|
||||
return _writer.getWrittenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
@@ -275,7 +275,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BITS_SIZE<ValueType>::value;
|
||||
constexpr size_t valueSize = details::BITS_SIZE<TValue>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
@@ -283,8 +283,8 @@ namespace bitsery {
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<ValueType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(ValueType), ValueType >(tmp);
|
||||
auto tmp = static_cast<TValue>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(TValue), TValue >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
@@ -294,29 +294,26 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
//overload for ValueType, for better performance
|
||||
void writeBitsInternal(const ValueType &v, size_t size) {
|
||||
//overload for TValue, for better performance
|
||||
void writeBitsInternal(const TValue &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BITS_SIZE<ValueType>::value) {
|
||||
auto tmp = static_cast<ValueType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(ValueType), ValueType>(tmp);
|
||||
_scratch >>= details::BITS_SIZE<ValueType>::value;
|
||||
_scratchBits -= details::BITS_SIZE<ValueType>::value;
|
||||
if (_scratchBits >= details::BITS_SIZE<TValue>::value) {
|
||||
auto tmp = static_cast<TValue>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(TValue), TValue>(tmp);
|
||||
_scratch >>= details::BITS_SIZE<TValue>::value;
|
||||
_scratchBits -= details::BITS_SIZE<TValue>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ValueType _MASK = std::numeric_limits<ValueType>::max();
|
||||
const TValue _MASK = std::numeric_limits<TValue>::max();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
BasicBufferWriter<Config>& _writer;
|
||||
TWriter& _writer;
|
||||
|
||||
};
|
||||
|
||||
//helper type
|
||||
using BufferWriter = BasicBufferWriter<DefaultConfig>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_WRITER_H
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
#ifndef BITSERY_COMMON_H
|
||||
#define BITSERY_COMMON_H
|
||||
|
||||
#include <vector>
|
||||
#include "traits/vector.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
/*
|
||||
@@ -40,12 +37,9 @@ namespace bitsery {
|
||||
//default configuration for buffer writing/reading operations
|
||||
struct DefaultConfig {
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
//this functionality allows to support backward/forward compatibility for any type
|
||||
//disabling it, saves 100+bytes per BufferReader/Writer and also reduces executable size
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
//buffer value type must be unsigned, currently only uint8_t supported
|
||||
//fixed size buffer type also supported, for faster serialization performance
|
||||
using BufferType = std::vector<uint8_t>;
|
||||
//this functionality allows to support backward/forward compatibility
|
||||
//however reading from streams is not supported, because this functionality requires random access to data buffer
|
||||
static constexpr bool BufferSessionsEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -24,19 +24,19 @@
|
||||
#ifndef BITSERY_DESERIALIZER_H
|
||||
#define BITSERY_DESERIALIZER_H
|
||||
|
||||
#include "common.h"
|
||||
#include "details/serialization_common.h"
|
||||
#include "buffer_reader.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename Config, bool BitPackingEnabled>
|
||||
template<typename TReader, bool BitPackingEnabled = false>
|
||||
class BasicDeserializer {
|
||||
public:
|
||||
using BPEnabledType = BasicDeserializer<Config, true>;
|
||||
using BPEnabledType = BasicDeserializer<TReader, true>;
|
||||
|
||||
explicit BasicDeserializer(BasicBufferReader<Config> &r, void* context = nullptr)
|
||||
explicit BasicDeserializer(TReader &r, void* context = nullptr)
|
||||
: _reader{r},
|
||||
_context{context}
|
||||
{};
|
||||
@@ -106,32 +106,29 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
void ext(T &obj, const Ext &extension, Fnc &&fnc) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.deserialize(*this, _reader, obj, std::forward<Fnc>(fnc));
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
void ext(T &obj, const Ext &extension) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.deserialize(*this, _reader, obj, [this](VType &v) { value<VSIZE>(v);});
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
void ext(T &obj, const Ext &extension) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.deserialize(*this, _reader, obj, [this](VType &v) { object(v); });
|
||||
};
|
||||
|
||||
@@ -148,20 +145,24 @@ namespace bitsery {
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(T &str, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&) overload without `maxSize` for static containers");
|
||||
size_t length;
|
||||
details::readSize(_reader, length, maxSize);
|
||||
details::ContainerTraits<T>::resize(str, length + (details::TextTraits<T>::addNUL ? 1u : 0u));
|
||||
traits::ContainerTraits<T>::resize(str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(T &str) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
size_t length;
|
||||
details::readSize(_reader, length, details::ContainerTraits<T>::size(str));
|
||||
details::readSize(_reader, length, traits::ContainerTraits<T>::size(str));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
|
||||
@@ -173,54 +174,65 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void container(T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
//fixed size containers
|
||||
|
||||
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
|
||||
void container(T &obj, Fnc &&fnc) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
@@ -300,8 +312,8 @@ namespace bitsery {
|
||||
private:
|
||||
|
||||
typename std::conditional<BitPackingEnabled,
|
||||
BitPackingReader<Config>,//by value
|
||||
BasicBufferReader<Config>&//by reference
|
||||
BitPackingReader<TReader>,//by value
|
||||
TReader&//by reference
|
||||
>::type _reader;
|
||||
void* _context;
|
||||
|
||||
@@ -342,9 +354,9 @@ namespace bitsery {
|
||||
auto begin = std::begin(str);
|
||||
//end of string, not end of container
|
||||
auto end = std::next(begin, length);
|
||||
procContainer<VSIZE>(begin, end, std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
procContainer<VSIZE>(begin, end, std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
//null terminated character at the end
|
||||
if (details::TextTraits<T>::addNUL)
|
||||
if (traits::TextTraits<T>::addNUL)
|
||||
*end = {};
|
||||
}
|
||||
|
||||
@@ -359,7 +371,7 @@ namespace bitsery {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
@@ -393,7 +405,17 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//helper type
|
||||
using Deserializer = BasicDeserializer<DefaultConfig, false>;
|
||||
template <typename TReader>
|
||||
using Deserializer = BasicDeserializer<TReader, false>;
|
||||
|
||||
//helper function that set ups all the basic steps and after deserialziation returns status
|
||||
template <typename Adapter, typename T, typename Config = DefaultConfig>
|
||||
std::pair<ReaderError, bool> startDeserialization(Adapter adapter, T& value) {
|
||||
BasicReader<Config, Adapter> br{std::move(adapter)};
|
||||
BasicDeserializer<BasicReader<Config, Adapter>> des{br};
|
||||
des.object(value);
|
||||
return {br.getError(), br.isCompletedSuccessfully()};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,16 @@
|
||||
#ifndef BITSERY_DETAILS_BOTH_COMMON_H
|
||||
#define BITSERY_DETAILS_BOTH_COMMON_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
enum class BufferReaderError {
|
||||
enum class ReaderError {
|
||||
NO_ERROR,
|
||||
BUFFER_OVERFLOW,
|
||||
INVALID_BUFFER_DATA
|
||||
DATA_OVERFLOW,
|
||||
INVALID_DATA
|
||||
};
|
||||
|
||||
namespace details {
|
||||
@@ -56,7 +57,7 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
r.setError(ReaderError::INVALID_DATA);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +30,12 @@
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include "both_common.h"
|
||||
#include "traits.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename I>
|
||||
struct BufferRange : std::pair<I, I> {
|
||||
using std::pair<I, I>::pair;
|
||||
I begin() const { return this->first; }
|
||||
I end() const { return this->second; }
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
@@ -105,6 +98,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
struct SCRATCH_TYPE {
|
||||
using type = NotDefinedType;
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -112,368 +106,29 @@ namespace bitsery {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SCRATCH_TYPE<uint16_t> {
|
||||
using type = uint32_t;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SCRATCH_TYPE<uint32_t> {
|
||||
using type = uint64_t;
|
||||
};
|
||||
// template<>
|
||||
// struct SCRATCH_TYPE<uint16_t> {
|
||||
// using type = uint32_t;
|
||||
// };
|
||||
//
|
||||
// template<>
|
||||
// struct SCRATCH_TYPE<uint32_t> {
|
||||
// using type = uint64_t;
|
||||
// };
|
||||
|
||||
template <typename Config>
|
||||
struct DisabledBufferSessionsWriter {
|
||||
template <typename TWriter>
|
||||
void begin(TWriter& ) {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
/*
|
||||
* class used by session reader, to access underlying iterators of buffer
|
||||
*/
|
||||
struct SessionAccess {
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& posIteratorRef(TReader& r) {
|
||||
return r._pos;
|
||||
}
|
||||
template <typename TWriter>
|
||||
void end(TWriter& ) {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& endIteratorRef(TReader& r) {
|
||||
return r._end;
|
||||
}
|
||||
template <typename TWriter>
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Config>
|
||||
struct DisabledBufferSessionsReader {
|
||||
template <typename TReader, typename TBufferContext>
|
||||
DisabledBufferSessionsReader(TReader& , TBufferContext& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TWriter>
|
||||
class BufferSessionsWriter {
|
||||
public:
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
auto range = writer.getWrittenRange();
|
||||
*sessionIt = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto range = writer.getWrittenRange();
|
||||
auto dataSize = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
range = writer.getWrittenRange();
|
||||
auto totalSize = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 2;//2 bytes for offset data
|
||||
if (sessionsOffset < 0x8000u) {
|
||||
writer.template writeBytes<2>(static_cast<uint16_t>(sessionsOffset));
|
||||
} else {
|
||||
//size doesnt fit in 2 bytes, write 4 bytes instead
|
||||
sessionsOffset+=2;
|
||||
uint16_t low = static_cast<uint16_t>(sessionsOffset);
|
||||
//mark most significant bit, that size is 4 bytes
|
||||
uint16_t high = static_cast<uint16_t>(0x8000u | (sessionsOffset >> 16));
|
||||
writer.template writeBytes<2>(low);
|
||||
writer.template writeBytes<2>(high);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex;
|
||||
};
|
||||
|
||||
template <typename TReader, typename TBufferContext>
|
||||
struct BufferSessionsReader {
|
||||
using TIterator = typename TReader::BufferIteratorType;
|
||||
|
||||
BufferSessionsReader(TReader& r, TBufferContext& bufferContext)
|
||||
:_reader{r},
|
||||
_begin{bufferContext._pos},
|
||||
_context{bufferContext}
|
||||
{
|
||||
}
|
||||
void begin() {
|
||||
if (_sessions.empty()) {
|
||||
if (!initializeSessions())
|
||||
return;
|
||||
}
|
||||
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_context._end);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_context._pos, _context._end) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_begin, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _context._end) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return;
|
||||
}
|
||||
_context._end = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_context._pos == _context._end || _reader.getError() == BufferReaderError::NO_ERROR)) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//getError() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_context._pos, _context._end);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_begin, _context._end));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
_context._pos = _context._end;
|
||||
//restore end position
|
||||
_context._end = _sessionsStack.top();
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _begin;
|
||||
TBufferContext& _context;
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
bool initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _context._pos;
|
||||
//read size
|
||||
if (std::distance(_context._pos, _context._end) < 2) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_context._end, -2);
|
||||
_context._pos = endSessionsSizesIt;
|
||||
size_t sessionsOffset{};
|
||||
uint16_t high;
|
||||
_reader.template readBytes<2>(high);
|
||||
|
||||
|
||||
if (high >= 0x8000u) {
|
||||
endSessionsSizesIt = std::next(endSessionsSizesIt, -2);
|
||||
_context._pos = endSessionsSizesIt;
|
||||
if (std::distance(_begin, _context._pos) < 0) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return false;
|
||||
}
|
||||
uint16_t low;
|
||||
_reader.template readBytes<2>(low);
|
||||
//mask out last bit
|
||||
high &= 0x7FFFu;
|
||||
sessionsOffset = static_cast<size_t>((high << 16) | low);
|
||||
|
||||
} else
|
||||
sessionsOffset = high;
|
||||
|
||||
auto bufferSize = std::distance(_begin, _context._end);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_context._pos = std::next(_context._end, -sessionsOffset);
|
||||
while (std::distance(_context._pos, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_context._pos = currPos;
|
||||
_context._end = std::next(_context._end, -sessionsOffset);
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//this class writes bytes and bits to underlying buffer, it has specializations for resizable and non-resizable buffers
|
||||
template<typename Buffer, bool isResizable>
|
||||
class WriteBufferContext {
|
||||
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class WriteBufferContext<Buffer, false> {
|
||||
public:
|
||||
using TValue = typename ContainerTraits<Buffer>::TValue;
|
||||
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
|
||||
|
||||
explicit WriteBufferContext(Buffer &buffer)
|
||||
: _buffer{buffer},
|
||||
_outIt{std::begin(buffer)},
|
||||
_end{std::end(buffer)}
|
||||
{
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
|
||||
BufferRange<TIterator> getWrittenRange() const {
|
||||
return BufferRange<TIterator>{std::begin(_buffer), _outIt};
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer &_buffer;
|
||||
TIterator _outIt;
|
||||
TIterator _end;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class WriteBufferContext<Buffer, true> {
|
||||
public:
|
||||
using TValue = typename ContainerTraits<Buffer>::TValue;
|
||||
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
|
||||
|
||||
explicit WriteBufferContext(Buffer &buffer)
|
||||
: _buffer{buffer}
|
||||
{
|
||||
//resize buffer immediately, because we need output iterator at valid position
|
||||
if (ContainerTraits<Buffer>::size(buffer) == 0u) {
|
||||
BufferContainerTraits<Buffer>::increaseBufferSize(_buffer);
|
||||
}
|
||||
getIterators(0);
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
} else {
|
||||
_outIt -= size;
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(_buffer), _outIt);
|
||||
//increase container size
|
||||
BufferContainerTraits<Buffer>::increaseBufferSize(_buffer);
|
||||
//restore iterators
|
||||
getIterators(static_cast<size_t>(pos));
|
||||
write(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
BufferRange<TIterator> getWrittenRange() const {
|
||||
return BufferRange<TIterator>{std::begin(_buffer), _outIt};
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void getIterators(size_t writePos) {
|
||||
_end = std::end(_buffer);
|
||||
_outIt = std::next(std::begin(_buffer), writePos);
|
||||
}
|
||||
Buffer &_buffer;
|
||||
TIterator _outIt;
|
||||
TIterator _end;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename Buffer>
|
||||
class ReadBufferContext {
|
||||
public:
|
||||
using TValue = typename ContainerTraits<Buffer>::TValue;
|
||||
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
|
||||
|
||||
ReadBufferContext(TIterator begin, TIterator end)
|
||||
:_pos{begin},
|
||||
_end{end}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = _pos;
|
||||
_pos += size;
|
||||
if (std::distance(_pos, _end) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
_pos -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (getError() == BufferReaderError::NO_ERROR)
|
||||
setError(BufferReaderError::BUFFER_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
BufferReaderError getError() const {
|
||||
auto res = std::distance(_end, _pos);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<BufferReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return BufferReaderError::NO_ERROR;
|
||||
}
|
||||
|
||||
void setError(BufferReaderError error) {
|
||||
_end = _pos;
|
||||
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
|
||||
std::advance(_pos, static_cast<size_t>(error));
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _pos == _end;
|
||||
}
|
||||
|
||||
TIterator _pos;
|
||||
TIterator _end;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#ifndef BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
#define BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
|
||||
#include "traits.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include <limits>
|
||||
|
||||
namespace bitsery {
|
||||
@@ -33,34 +33,34 @@ namespace bitsery {
|
||||
//for contigous arrays of fundamenal types, memcpy will be applied
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
details::IsFundamentalType<typename details::ContainerTraits<T>::TValue>::value
|
||||
&& details::ContainerTraits<T>::isResizable
|
||||
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TValue = typename details::ContainerTraits<T>::TValue;
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template container<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!details::IsFundamentalType<typename details::ContainerTraits<T>::TValue>::value
|
||||
&& details::ContainerTraits<T>::isResizable
|
||||
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.container(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
details::IsFundamentalType<typename details::ContainerTraits<T>::TValue>::value
|
||||
&& !details::ContainerTraits<T>::isResizable
|
||||
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& !traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c) {
|
||||
using TValue = typename details::ContainerTraits<T>::TValue;
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template container<sizeof(TValue)>(c);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!details::IsFundamentalType<typename details::ContainerTraits<T>::TValue>::value
|
||||
&& !details::ContainerTraits<T>::isResizable
|
||||
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& !traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c) {
|
||||
s.container(c);
|
||||
@@ -69,16 +69,16 @@ namespace bitsery {
|
||||
//overloads for text processing to apply maxSize
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
details::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
traits::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
void processText(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TValue = typename details::ContainerTraits<T>::TValue;
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!details::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
!traits::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
void processText(S &s, T &c) {
|
||||
using TValue = typename details::ContainerTraits<T>::TValue;
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c);
|
||||
}
|
||||
|
||||
|
||||
78
include/bitsery/details/not_defined_type.h
Normal file
78
include/bitsery/details/not_defined_type.h
Normal file
@@ -0,0 +1,78 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 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_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
#define BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
//this type is used to show clearer error messages
|
||||
struct NotDefinedType {
|
||||
//just swallow anything that is passed during creating
|
||||
template <typename ... T>
|
||||
NotDefinedType(T&& ...){};
|
||||
NotDefinedType() = default;
|
||||
//define operators so that we also swallow deeper errors, to reduce error stack
|
||||
//this time will be used as iterator, so define all operators nessesarry to work with iterators
|
||||
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
|
||||
return true;
|
||||
}
|
||||
friend bool operator != (const NotDefinedType&, const NotDefinedType&) {
|
||||
return false;
|
||||
}
|
||||
NotDefinedType& operator += (int) {
|
||||
return *this;
|
||||
}
|
||||
NotDefinedType& operator -= (int) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int operator - (const NotDefinedType&, const NotDefinedType&) {
|
||||
return 0;
|
||||
}
|
||||
int& operator*() {
|
||||
return data;
|
||||
}
|
||||
int data;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsDefined:public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
//define iterator traits to work with standart algorithms
|
||||
template <>
|
||||
struct iterator_traits<bitsery::details::NotDefinedType> {
|
||||
using difference_type = int;
|
||||
using value_type = int;
|
||||
using pointer = int*;
|
||||
using reference = int&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
};
|
||||
}
|
||||
#endif //BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
@@ -24,7 +24,9 @@
|
||||
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "both_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
@@ -40,6 +42,19 @@ namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
//helper types for error handling
|
||||
template <typename T>
|
||||
struct IsContainerTraitsDefined:public IsDefined<typename traits::ContainerTraits<T>::TValue> {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsTextTraitsDefined:public IsDefined<typename traits::TextTraits<T>::TValue> {
|
||||
};
|
||||
|
||||
template <typename Ext, typename T>
|
||||
struct IsExtensionTraitsDefined:public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
|
||||
};
|
||||
|
||||
//used for extensions, when extension TValue = void
|
||||
struct DummyType {
|
||||
};
|
||||
|
||||
209
include/bitsery/details/sessions.h
Normal file
209
include/bitsery/details/sessions.h
Normal file
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// Created by fraillt on 17.10.5.
|
||||
//
|
||||
|
||||
#ifndef BITSERY_BUFFER_SESSIONS_H
|
||||
#define BITSERY_BUFFER_SESSIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include "buffer_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace session {
|
||||
/*
|
||||
* writer/reader implementations that disable session support
|
||||
*/
|
||||
template <typename TWriter>
|
||||
struct DisabledSessionsWriter {
|
||||
void begin(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void end(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct DisabledSessionsReader {
|
||||
template <typename TBufferContext>
|
||||
DisabledSessionsReader(TReader& , TBufferContext& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* writer/reader real implementations
|
||||
* sessions reading requires to have random access iterators, so it cannot be used with streams
|
||||
*/
|
||||
template <typename TWriter>
|
||||
class SessionsWriter {
|
||||
public:
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
*sessionIt = writer.getWrittenBytesCount();
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto dataSize = writer.getWrittenBytesCount();
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
auto totalSize = writer.getWrittenBytesCount();
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 4;//4 bytes for offset data
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(sessionsOffset));
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex;
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct SessionsReader {
|
||||
using TIterator = typename TReader::TIterator;
|
||||
|
||||
template <typename InputAdapter>
|
||||
SessionsReader(TReader& r, InputAdapter& adapter)
|
||||
:_reader{r},
|
||||
_beginIt{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_posItRef{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_endItRef{details::SessionAccess::endIteratorRef<InputAdapter, TIterator>(adapter)}
|
||||
{
|
||||
}
|
||||
void begin() {
|
||||
if (_sessions.empty()) {
|
||||
if (!initializeSessions())
|
||||
return;
|
||||
}
|
||||
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_endItRef);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_posItRef, _endItRef) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_beginIt, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _endItRef) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
return;
|
||||
}
|
||||
_endItRef = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_posItRef == _endItRef || _reader.getError() == ReaderError::NO_ERROR)) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//getError() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_posItRef, _endItRef);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_beginIt, _endItRef));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
_posItRef = _endItRef;
|
||||
//restore end position
|
||||
_endItRef = _sessionsStack.top();
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _beginIt;
|
||||
TIterator& _posItRef;
|
||||
TIterator& _endItRef;
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
bool initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _posItRef;
|
||||
//read size
|
||||
if (std::distance(_posItRef, _endItRef) < 4) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_endItRef, -4);
|
||||
_posItRef = endSessionsSizesIt;
|
||||
uint32_t sessionsOffset{};
|
||||
_reader.template readBytes<4>(sessionsOffset);
|
||||
|
||||
auto bufferSize = std::distance(_beginIt, _endItRef);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_posItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
while (std::distance(_posItRef, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_posItRef = currPos;
|
||||
_endItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_SESSIONS_H
|
||||
@@ -58,9 +58,9 @@ namespace bitsery {
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
assert(details::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
auto index = details::findEntropyIndex(obj, _values);
|
||||
s.ext(index, ext::ValueRange<size_t>{0u, details::ContainerTraits<TContainer>::size(_values)});
|
||||
s.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
s.align();
|
||||
if (!index)
|
||||
@@ -69,9 +69,9 @@ namespace bitsery {
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
assert(details::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
size_t index{};
|
||||
d.ext(index, ext::ValueRange<size_t>{0u, details::ContainerTraits<TContainer>::size(_values)});
|
||||
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
d.align();
|
||||
if (index)
|
||||
@@ -86,7 +86,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename TContainer, typename T>
|
||||
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
|
||||
using TValue = T;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Growable, T> {
|
||||
using TValue = T;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdMap, T> {
|
||||
using TValue = void;
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdOptional, T> {
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdQueue, T> {
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdSet, T> {
|
||||
using TValue = typename T::key_type;
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace bitsery {
|
||||
reader.readBits(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v), _range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
reader.setError(ReaderError::INVALID_DATA);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ValueRange<T>, T> {
|
||||
using TValue = void;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_FLEXIBLE_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "bitsery/details/flexible_common.h"
|
||||
#include "details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include "bitsery/ext/std_map.h"
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_UNORDERED_MAP_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "bitsery/ext/std_map.h"
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
|
||||
@@ -24,18 +24,18 @@
|
||||
#ifndef BITSERY_SERIALIZER_H
|
||||
#define BITSERY_SERIALIZER_H
|
||||
|
||||
#include "common.h"
|
||||
#include "details/serialization_common.h"
|
||||
#include "buffer_writer.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename Config, bool BitPackingEnabled>
|
||||
template<typename TWriter, bool BitPackingEnabled = false>
|
||||
class BasicSerializer {
|
||||
public:
|
||||
using BPEnabledType = BasicSerializer<Config, true>;
|
||||
using BPEnabledType = BasicSerializer<TWriter, true>;
|
||||
|
||||
explicit BasicSerializer(BasicBufferWriter<Config> &w, void* context = nullptr)
|
||||
explicit BasicSerializer(TWriter &w, void* context = nullptr)
|
||||
: _writer{w},
|
||||
_context{context}
|
||||
{};
|
||||
@@ -104,32 +104,29 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
void ext(const T &obj, const Ext &extension, Fnc &&fnc) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.serialize(*this, _writer, obj, std::forward<Fnc>(fnc));
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
void ext(const T &obj, const Ext &extension) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.serialize(*this, _writer, obj, [this](VType &v) { value<VSIZE>(v); });
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
void ext(const T &obj, const Ext &extension) {
|
||||
static_assert(details::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
static_assert(BitPackingEnabled || !details::ExtensionTraits<Ext,T>::BitPackingRequired,
|
||||
"Extension requires bit-packing to be enabled, (call `enableBitPacking`)");
|
||||
extension.serialize(*this, _writer, obj, [this](VType &v) { object(v); });
|
||||
};
|
||||
|
||||
@@ -147,16 +144,20 @@ namespace bitsery {
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(const T &str, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use text(const T&) overload without `maxSize` for static container");
|
||||
procText<VSIZE>(str, maxSize);
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(const T &str) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use text(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
procText<VSIZE>(str, details::ContainerTraits<T>::size(str));
|
||||
procText<VSIZE>(str, traits::ContainerTraits<T>::size(str));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -167,10 +168,11 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void container(const T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, Fnc) overload without `maxSize` for static containers");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
@@ -178,21 +180,25 @@ namespace bitsery {
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(const T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&) overload without `maxSize` for static containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(const T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&) overload without `maxSize` for static containers");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
@@ -202,22 +208,28 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
|
||||
void container(const T &obj, Fnc &&fnc) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(const T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(const T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
@@ -297,8 +309,8 @@ namespace bitsery {
|
||||
private:
|
||||
|
||||
typename std::conditional<BitPackingEnabled,
|
||||
BitPackingWriter<Config>,//by value
|
||||
BasicBufferWriter<Config>&//by reference
|
||||
BitPackingWriter<TWriter>,//by value
|
||||
TWriter&//by reference
|
||||
>::type _writer;
|
||||
void* _context;
|
||||
|
||||
@@ -333,11 +345,11 @@ namespace bitsery {
|
||||
//process text,
|
||||
template<size_t VSIZE, typename T>
|
||||
void procText(const T& str, size_t maxSize) {
|
||||
auto length = details::TextTraits<T>::length(str);
|
||||
assert((length + (details::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
|
||||
auto length = traits::TextTraits<T>::length(str);
|
||||
assert((length + (traits::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
|
||||
details::writeSize(_writer, length);
|
||||
auto begin = std::begin(str);
|
||||
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
|
||||
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
};
|
||||
|
||||
//process object types
|
||||
@@ -385,7 +397,27 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//helper type
|
||||
using Serializer = BasicSerializer<DefaultConfig, false>;
|
||||
template <typename TWriter>
|
||||
using Serializer = BasicSerializer<TWriter, false>;
|
||||
|
||||
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
|
||||
template <typename Adapter, typename T, typename Config = DefaultConfig>
|
||||
size_t startSerialization(Adapter adapter, const T& value) {
|
||||
BasicWriter<Config, Adapter> bw{std::move(adapter)};
|
||||
BasicSerializer<BasicWriter<Config, Adapter>> ser{bw};
|
||||
ser.object(value);
|
||||
bw.flush();
|
||||
return bw.getWrittenBytesCount();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
size_t startMeasureSize(const T& value) {
|
||||
MeasureSize ms{};
|
||||
BasicSerializer<MeasureSize> ser {ms};
|
||||
ser.object(value);
|
||||
ms.flush();
|
||||
return ms.getWrittenBytesCount();
|
||||
}
|
||||
|
||||
}
|
||||
#endif //BITSERY_SERIALIZER_H
|
||||
|
||||
@@ -24,19 +24,19 @@
|
||||
#ifndef BITSERY_TRAITS_STD_ARRAY_H
|
||||
#define BITSERY_TRAITS_STD_ARRAY_H
|
||||
|
||||
#include "helper/std_defaults.h"
|
||||
#include "core/std_defaults.h"
|
||||
#include <array>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T, size_t N>
|
||||
struct ContainerTraits<std::array<T, N>>
|
||||
:public StdContainer<std::array<T, N>, false, true> {};
|
||||
|
||||
template<typename T, size_t N>
|
||||
struct BufferContainerTraits<std::array<T, N>>
|
||||
:public StdContainerForBuffer<std::array<T, N>> {};
|
||||
struct BufferAdapterTraits<std::array<T, N>>
|
||||
:public StdContainerForBufferAdapter<std::array<T, N>> {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
#ifndef BITSERY_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
#define BITSERY_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
|
||||
#include "traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
/*
|
||||
* these are helper types, to easier write specializations for std types
|
||||
@@ -55,12 +57,13 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template <typename T, bool Resizable = ContainerTraits<T>::isResizable>
|
||||
struct StdContainerForBuffer {
|
||||
struct StdContainerForBufferAdapter {
|
||||
using TIterator = typename T::iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct StdContainerForBuffer<T, true> {
|
||||
struct StdContainerForBufferAdapter<T, true> {
|
||||
|
||||
static void increaseBufferSize(T& container) {
|
||||
//use default implementation behaviour;
|
||||
@@ -70,6 +73,7 @@ namespace bitsery {
|
||||
container.resize(container.capacity());
|
||||
}
|
||||
using TIterator = typename T::iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -20,13 +20,14 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_TRAITS_H
|
||||
#define BITSERY_DETAILS_TRAITS_H
|
||||
#ifndef BITSERY_TRAITS_CORE_TRAITS_H
|
||||
#define BITSERY_TRAITS_CORE_TRAITS_H
|
||||
|
||||
#include <type_traits>
|
||||
#include "../../details/not_defined_type.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
/*
|
||||
* core library traits, used to extend library for custom types
|
||||
@@ -39,27 +40,21 @@ namespace bitsery {
|
||||
// eg.: extension4b>(obj, myextension{}) will call s.value4b(obj) for TValue
|
||||
// or extesion(obj, myextension{}) will call s.object(obj) for TValue
|
||||
//when this is void, it will compile, but value and object overloads will do nothing.
|
||||
using TValue = void;
|
||||
using TValue = details::NotDefinedType;
|
||||
|
||||
|
||||
//specify if extension required bitpacking operations
|
||||
//if current serialization instance is not bit-packing enabled,
|
||||
//then new instance will be created with bit-packing enabled,
|
||||
//and bits will be flushed automaticaly after extension finish executing.
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
//does extension support ext<N>(...) syntax, by calling value<N> with TValue
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
//does extension support ext(...) syntax, by calling object with TValue
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
//does extension support ext(..., lambda)
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
//primary traits for containers
|
||||
template<typename T>
|
||||
struct ContainerTraits {
|
||||
|
||||
using TValue = void;
|
||||
using TValue = details::NotDefinedType;
|
||||
|
||||
static constexpr bool isResizable = false;
|
||||
//contiguous arrays has oppurtunity to memcpy whole buffer directly when using funtamental types
|
||||
@@ -106,7 +101,7 @@ namespace bitsery {
|
||||
//traits for text, default adds null-terminated character at the end
|
||||
template<typename T>
|
||||
struct TextTraits {
|
||||
|
||||
using TValue = details::NotDefinedType;
|
||||
//if container is not null-terminated by default, add NUL at the end
|
||||
static constexpr bool addNUL = true;
|
||||
|
||||
@@ -118,32 +113,47 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
//traits only for buffer reader/writer
|
||||
//traits only for buffer adapters
|
||||
template <typename T>
|
||||
struct BufferContainerTraits {
|
||||
struct BufferAdapterTraits {
|
||||
//this function is only applies to resizable containers
|
||||
|
||||
//this function is only used by BufferWriter, when writing data to buffer,
|
||||
//this function is only used by Writer, when writing data to buffer,
|
||||
//it is called only current buffer size is not enough to write.
|
||||
//it is used to dramaticaly improve performance by updating buffer directly
|
||||
//instead of using back_insert_iterator to append each byte to buffer.
|
||||
//thats why BufferWriter return range iterators
|
||||
//thats why Writer return range iterators
|
||||
|
||||
static void increaseBufferSize(T& container) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Define BufferContainerTraits or include from <bitsery/traits/...> to use as buffer");
|
||||
"Define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer adapter container");
|
||||
}
|
||||
|
||||
using TIterator = void;
|
||||
using TIterator = details::NotDefinedType;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
//specialization for c-style buffer
|
||||
template <typename T, size_t N>
|
||||
struct BufferContainerTraits<T[N]> {
|
||||
struct BufferAdapterTraits<T[N]> {
|
||||
using TIterator = T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
//specialization for pointer type buffer
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<const T*> {
|
||||
using TIterator = const T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<T*> {
|
||||
using TIterator = T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_TRAITS_H
|
||||
#endif //BITSERY_TRAITS_CORE_TRAITS_H
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef BITSERY_TRAITS_STD_DEQUE_H
|
||||
#define BITSERY_TRAITS_STD_DEQUE_H
|
||||
|
||||
#include "helper/std_defaults.h"
|
||||
#include "core/std_defaults.h"
|
||||
#include <deque>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::deque<TArgs...>>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::forward_list<TArgs...>> {
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef BITSERY_TRAITS_STD_LIST_H
|
||||
#define BITSERY_TRAITS_STD_LIST_H
|
||||
|
||||
#include "helper/std_defaults.h"
|
||||
#include "core/std_defaults.h"
|
||||
#include <list>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::list<TArgs...>>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef BITSERY_TRAITS_STD_STRING_H
|
||||
#define BITSERY_TRAITS_STD_STRING_H
|
||||
|
||||
#include "helper/std_defaults.h"
|
||||
#include "core/std_defaults.h"
|
||||
#include <string>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
|
||||
// specialization for string, because string is already included for std::char_traits
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace bitsery {
|
||||
|
||||
template <typename ... TArgs>
|
||||
struct TextTraits<std::basic_string<TArgs...>> {
|
||||
|
||||
using TValue = typename ContainerTraits<std::basic_string<TArgs...>>::TValue;
|
||||
//string is automatically null-terminated
|
||||
static constexpr bool addNUL = false;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace bitsery {
|
||||
//specialization for c-array
|
||||
template <typename T, size_t N>
|
||||
struct TextTraits<T[N]> {
|
||||
|
||||
using TValue = T;
|
||||
static constexpr bool addNUL = true;
|
||||
|
||||
static size_t length(const T (&container)[N]) {
|
||||
@@ -61,8 +61,8 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferContainerTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainerForBuffer<std::basic_string<TArgs...>> {};
|
||||
struct BufferAdapterTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::basic_string<TArgs...>> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
#ifndef BITSERY_TRAITS_STD_VECTOR_H
|
||||
#define BITSERY_TRAITS_STD_VECTOR_H
|
||||
|
||||
#include "helper/std_defaults.h"
|
||||
#include "core/std_defaults.h"
|
||||
#include <vector>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::vector<TArgs...>>
|
||||
:public StdContainer<std::vector<TArgs...>, true, true> {};
|
||||
@@ -40,8 +40,8 @@ namespace bitsery {
|
||||
:public StdContainer<std::vector<bool, Allocator>, true, false> {};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferContainerTraits<std::vector<TArgs...>>
|
||||
:public StdContainerForBuffer<std::vector<TArgs...>> {};
|
||||
struct BufferAdapterTraits<std::vector<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::vector<TArgs...>> {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
using bitsery::DefaultConfig;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
constexpr EndiannessType getInverseEndianness(EndiannessType e) {
|
||||
return e == EndiannessType::LittleEndian
|
||||
@@ -50,8 +50,10 @@ struct IntegralTypes {
|
||||
int8_t e;
|
||||
};
|
||||
|
||||
using InverseReader = bitsery::BasicReader<InverseEndiannessConfig, InputAdapter >;
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
|
||||
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
//fill initial values
|
||||
IntegralTypes src{};
|
||||
src.a = 0x1122334455667788;
|
||||
@@ -70,7 +72,7 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBytes<8>(src.a);
|
||||
bw.writeBytes<4>(src.b);
|
||||
bw.writeBytes<2>(src.c);
|
||||
@@ -78,7 +80,7 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
bw.writeBytes<1>(src.e);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<4>(res.b);
|
||||
@@ -93,25 +95,25 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
EXPECT_THAT(res.e, Eq(resInv.e));
|
||||
}
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBuffer1ByteValuesThenEndiannessIsIgnored) {
|
||||
TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
|
||||
//fill initial values
|
||||
constexpr size_t SIZE = 4;
|
||||
uint8_t src[SIZE] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t res[SIZE] = {};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<1>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
br.readBuffer<1>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
}
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBufferMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
//fill initial values
|
||||
constexpr size_t SIZE = 4;
|
||||
uint16_t src[SIZE] = {0xAA00, 0xBB11, 0xCC22, 0xDD33};
|
||||
@@ -119,11 +121,11 @@ TEST(BufferEndianness, WhenWriteBufferMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
uint16_t res[SIZE] = {};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<2>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
br.readBuffer<2>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
@@ -143,9 +145,7 @@ struct IntegralUnsignedTypes {
|
||||
uint8_t d;
|
||||
};
|
||||
|
||||
TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
|
||||
//fill initial values
|
||||
static_assert(sizeof(bitsery::details::ContainerTraits<DefaultConfig::BufferType>::TValue) == 1, "currently only 1 byte size, value size is supported");
|
||||
TEST(DataEndianness, WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
|
||||
//fill initial values
|
||||
constexpr IntegralUnsignedTypes src {
|
||||
0x0000334455667788,//bits 19
|
||||
@@ -160,16 +160,16 @@ TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedB
|
||||
constexpr size_t dBITS = getBits(src.d) + 2;
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
bitsery::BitPackingWriter<DefaultConfig> bpw{bw};
|
||||
Writer bw{buf};
|
||||
bitsery::BitPackingWriter<Writer> bpw{bw};
|
||||
bpw.writeBits(src.a, aBITS);
|
||||
bpw.writeBits(src.b, bBITS);
|
||||
bpw.writeBits(src.c, cBITS);
|
||||
bpw.writeBits(src.d, dBITS);
|
||||
bpw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bpw.getWrittenRange()};
|
||||
bitsery::BitPackingReader<InverseEndiannessConfig> bpr{br};
|
||||
InverseReader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<InverseReader> bpr{br};
|
||||
IntegralUnsignedTypes res{};
|
||||
bpr.readBits(res.a, aBITS);
|
||||
bpr.readBits(res.b, bBITS);
|
||||
|
||||
@@ -22,18 +22,15 @@
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
|
||||
using BitPackingWriter = bitsery::BitPackingWriter<bitsery::DefaultConfig>;
|
||||
using BitPackingReader = bitsery::BitPackingReader<bitsery::DefaultConfig>;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
using BitPackingWriter = bitsery::BitPackingWriter<Writer>;
|
||||
using BitPackingReader = bitsery::BitPackingReader<Reader>;
|
||||
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
uint32_t a;
|
||||
@@ -50,9 +47,9 @@ constexpr size_t getBits(T v) {
|
||||
|
||||
// *** bits operations
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
bpw.writeBits(std::numeric_limits<uint64_t>::max(), 64);
|
||||
bpw.writeBits(std::numeric_limits<uint32_t>::max(), 32);
|
||||
@@ -60,7 +57,7 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
bpw.writeBits(std::numeric_limits<uint8_t>::max(), 8);
|
||||
bpw.flush();
|
||||
|
||||
BufferReader br{bpw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
uint64_t v64{};
|
||||
uint32_t v32{};
|
||||
@@ -77,7 +74,7 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
EXPECT_THAT(v8, Eq(std::numeric_limits<uint8_t>::max()));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
|
||||
//setup data
|
||||
constexpr IntegralUnsignedTypes data{
|
||||
485454,//bits 19
|
||||
@@ -95,7 +92,7 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(data.a, aBITS);
|
||||
@@ -104,11 +101,11 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
|
||||
bpw.writeBits(data.d, dBITS);
|
||||
bpw.writeBits(data.e, eBITS);
|
||||
bpw.flush();
|
||||
auto range = bpw.getWrittenRange();
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(bytesCount));
|
||||
EXPECT_THAT(writtenSize, Eq(bytesCount));
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr{br};
|
||||
|
||||
IntegralUnsignedTypes res{};
|
||||
@@ -127,48 +124,48 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, BufferSizeIsCountedPerByteNotPerBit) {
|
||||
TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(7u,3);
|
||||
bpw.flush();
|
||||
auto range = bpw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1));
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr{br};
|
||||
uint16_t tmp;
|
||||
bpr.readBits(tmp,4);
|
||||
bpr.readBits(tmp,2);
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
|
||||
//part of next byte
|
||||
BufferReader br1{range};
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
bpr1.readBits(tmp,7);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
|
||||
//bigger than byte
|
||||
BufferReader br2{range};
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr2{br2};
|
||||
bpr2.readBits(tmp,9);
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(3u, 2);
|
||||
@@ -183,52 +180,52 @@ TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
bpw.flush();
|
||||
|
||||
unsigned char tmp;
|
||||
BufferReader br{bpw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(tmp, Eq(3u));
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
bpr.align();
|
||||
bpr.align();
|
||||
EXPECT_THAT(tmp, Eq(7u));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
|
||||
bpr.readBits(tmp,4);
|
||||
EXPECT_THAT(tmp, Eq(15u));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
|
||||
//write 2 bits and align
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
bpw.flush();
|
||||
auto range = bpw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1));
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
unsigned char tmp;
|
||||
BufferReader br1{range};
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
//read aligned bits
|
||||
bpr1.readBits(tmp,6);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
|
||||
BufferReader br2{range};
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr2{br2};
|
||||
//read 2 bits
|
||||
bpr2.readBits(tmp,2);
|
||||
bpr2.align();
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
}
|
||||
|
||||
|
||||
@@ -243,7 +240,7 @@ struct IntegralTypes {
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.a = -4894541654564;
|
||||
@@ -256,7 +253,7 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
@@ -264,11 +261,11 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
bw.writeBytes<1>(data.e);
|
||||
bw.writeBuffer<1>(data.f, 2);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
auto writtenSize = bw.getWrittenBytesCount();
|
||||
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(18));
|
||||
EXPECT_THAT(writtenSize, Eq(18));
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<4>(res.b);
|
||||
br.readBytes<2>(res.c);
|
||||
@@ -276,7 +273,7 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<1>(res.e);
|
||||
br.readBuffer<1>(res.f, 2);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
//assert results
|
||||
|
||||
EXPECT_THAT(data.a, Eq(res.a));
|
||||
@@ -288,69 +285,68 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ReadWriteBufferFncCanAcceptSignedData) {
|
||||
TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br1{bw.getWrittenRange()};
|
||||
Reader br1{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) {
|
||||
TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
bpw.writeBits(15u, 4);
|
||||
bpw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bpw.writeBits(12u, 4);
|
||||
bpw.flush();
|
||||
auto range = bpw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(sizeof(src) + 1));
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(sizeof(src) + 1));
|
||||
|
||||
//read from buffer
|
||||
BufferReader br1{range};
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
uint8_t tmp{};
|
||||
bpr1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(15));
|
||||
bpr1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
bpr1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(12));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) {
|
||||
TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) {
|
||||
//setup data
|
||||
int16_t data[2]{0x0000, 0x7FFF};
|
||||
int16_t res[2]{};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
bpw.writeBits(2u, 2);
|
||||
bpw.writeBytes<2>(data[0]);
|
||||
bpw.writeBytes<2>(data[1]);
|
||||
bpw.align();
|
||||
bpw.flush();
|
||||
auto range = bpw.getWrittenRange();
|
||||
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
uint8_t tmp{};
|
||||
bpr.readBits(tmp, 2);
|
||||
@@ -362,4 +358,3 @@ TEST(BufferBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsO
|
||||
EXPECT_THAT(res[1], Eq(data[1]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,15 +22,11 @@
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
using testing::Eq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
@@ -41,48 +37,48 @@ struct IntegralTypes {
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(BufferReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
TEST(DataReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
}
|
||||
|
||||
TEST(BufferReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
TEST(DataReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
br.readBytes<1>(a);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
}
|
||||
|
||||
|
||||
TEST(BufferReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
TEST(DataReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.b = 94545646;
|
||||
@@ -91,59 +87,59 @@ TEST(BufferReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors)
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
IntegralTypes res;
|
||||
br.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
br.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
|
||||
BufferReader br1{bw.getWrittenRange()};
|
||||
Reader br1{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
br1.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
}
|
||||
|
||||
TEST(BufferReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
bitsery::BitPackingReader<bitsery::DefaultConfig> bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<Reader> bpr{br};
|
||||
int32_t c;
|
||||
bpr.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
|
||||
int16_t r1= {-645};
|
||||
uint32_t r2[2] = {54898,87854};
|
||||
|
||||
@@ -25,19 +25,18 @@
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using testing::Eq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
using SessionsEnabledWriter = bitsery::BasicWriter<SessionsEnabledConfig, OutputAdapter>;
|
||||
using SessionsEnabledReader = bitsery::BasicReader<SessionsEnabledConfig, InputAdapter>;
|
||||
|
||||
TEST(BufferReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidBufferDataError) {
|
||||
TEST(DataReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidDataError) {
|
||||
SerializationContext ctx;
|
||||
std::string tmp = "larger text then allowed";
|
||||
ctx.createSerializer().text1b(tmp,100);
|
||||
ctx.createDeserializer().text1b(tmp, 10);
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) {
|
||||
TEST(DataReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.value1b(uint8_t{1});
|
||||
@@ -48,28 +47,28 @@ TEST(BufferReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDa
|
||||
EXPECT_THAT(res, Eq(true));
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(false));
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingAlignHasNonZerosThenInvalidBufferDataError) {
|
||||
TEST(DataReadingErrors, WhenReadingAlignHasNonZerosThenInvalidDataError) {
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
uint8_t tmp{0xFF};
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.flush();
|
||||
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
bitsery::BitPackingReader<bitsery::DefaultConfig> bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<Reader> bpr{br};
|
||||
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBufferError) {
|
||||
TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataError) {
|
||||
uint8_t tmp{0xFF};
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<1>(tmp);
|
||||
@@ -77,7 +76,7 @@ TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBuffe
|
||||
bw.endSession();
|
||||
}
|
||||
bw.flush();
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<1>(tmp);
|
||||
@@ -86,37 +85,37 @@ TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBuffe
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
}
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
|
||||
TEST(BufferReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidBufferData) {
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidData) {
|
||||
uint8_t tmp1{0xFF};
|
||||
Buffer buf1{};
|
||||
BufferWriter bw1{buf1};
|
||||
SessionsEnabledWriter bw1{buf1};
|
||||
bw1.writeBytes<1>(tmp1);
|
||||
bw1.flush();
|
||||
BufferReader br1{bw1.getWrittenRange()};
|
||||
SessionsEnabledReader br1{InputAdapter{buf1.begin(), bw1.getWrittenBytesCount()}};
|
||||
br1.beginSession();
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
|
||||
Buffer buf2{};
|
||||
BufferWriter bw2{buf2};
|
||||
SessionsEnabledWriter bw2{buf2};
|
||||
uint16_t tmp2{0x8000};
|
||||
bw2.writeBytes<2>(tmp2);
|
||||
bw2.flush();
|
||||
BufferReader br2{bw2.getWrittenRange()};
|
||||
SessionsEnabledReader br2{InputAdapter{buf2.begin(), bw2.getWrittenBytesCount()}};
|
||||
br2.beginSession();
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidBufferData) {
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidData) {
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<2>(uint16_t{10});
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
br.beginSession();
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
}
|
||||
|
||||
@@ -21,38 +21,27 @@
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/details/serialization_common.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
using bitsery::DefaultConfig;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct FixedBufferConfig: public DefaultConfig {
|
||||
using BufferType = std::array<uint8_t, 100>;
|
||||
};
|
||||
|
||||
struct NonFixedBufferConfig: public DefaultConfig {
|
||||
using BufferType = std::vector<uint8_t>;
|
||||
};
|
||||
|
||||
template <typename Config>
|
||||
class BufferWriting:public testing::Test {
|
||||
template <typename BufType>
|
||||
class DataWriting:public testing::Test {
|
||||
public:
|
||||
using type = Config;
|
||||
|
||||
using TWriter = bitsery::BasicWriter<bitsery::DefaultConfig, bitsery::OutputBufferAdapter<BufType>>;
|
||||
using TBuffer = BufType;
|
||||
};
|
||||
|
||||
using BufferWritingConfigs = ::testing::Types<
|
||||
NonFixedBufferConfig,
|
||||
FixedBufferConfig>;
|
||||
using NonFixedContainer = std::vector<uint8_t>;
|
||||
using FixedContainer = std::array<uint8_t, 100>;
|
||||
|
||||
TYPED_TEST_CASE(BufferWriting, BufferWritingConfigs);
|
||||
using ContainerTypes = ::testing::Types<FixedContainer,NonFixedContainer>;
|
||||
|
||||
TYPED_TEST_CASE(DataWriting, ContainerTypes);
|
||||
|
||||
static constexpr size_t DATA_SIZE = 14u;
|
||||
|
||||
@@ -67,50 +56,51 @@ void writeData(BW& bw) {
|
||||
bw.template writeBytes<4>(tmp5);
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, GetWrittenRangeReturnsBeginEndIterators) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
writeData(bw);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), DATA_SIZE);
|
||||
auto writtenSize = bw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize, DATA_SIZE);
|
||||
EXPECT_THAT(buf.size(), ::testing::Ge(DATA_SIZE));
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
bitsery::BitPackingWriter<Config> bpw{bw};
|
||||
TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::BitPackingWriter<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
auto range1 = bpw.getWrittenRange();
|
||||
auto writtenSize1 = bpw.getWrittenBytesCount();
|
||||
bpw.flush();
|
||||
auto range2 = bpw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(0));
|
||||
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
|
||||
auto writtenSize2 = bpw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(0));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
bitsery::BitPackingWriter<Config> bpw{bw};
|
||||
TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::BitPackingWriter<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
auto range1 = bpw.getWrittenRange();
|
||||
auto writtenSize1 = bpw.getWrittenBytesCount();
|
||||
bpw.flush();
|
||||
auto range2 = bpw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(1));
|
||||
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
|
||||
auto writtenSize2 = bpw.getWrittenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(1));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferWrittingNonFixedBuffer, BufferIsAlwaysResizedToCapacity) {
|
||||
using Buffer = typename NonFixedBufferConfig::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<NonFixedBufferConfig> bw{buf};
|
||||
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity) {
|
||||
NonFixedContainer buf{};
|
||||
bitsery::BasicWriter<bitsery::DefaultConfig, bitsery::OutputBufferAdapter<NonFixedContainer>> bw{buf};
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
uint32_t tmp{};
|
||||
bw.writeBytes<4>(tmp);
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
using testing::Eq;
|
||||
|
||||
template <bool BitPackingEnabled>
|
||||
using Serializer = bitsery::BasicSerializer<bitsery::DefaultConfig, BitPackingEnabled>;
|
||||
using Serializer = bitsery::BasicSerializer<Writer, BitPackingEnabled>;
|
||||
|
||||
template <bool BitPackingEnabled>
|
||||
using Deserializer = bitsery::BasicDeserializer<bitsery::DefaultConfig, BitPackingEnabled>;
|
||||
using Deserializer = bitsery::BasicDeserializer<Reader, BitPackingEnabled>;
|
||||
|
||||
|
||||
TEST(SerializeBooleans, BoolAsBit) {
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
TContainer res{};
|
||||
|
||||
size_t getExpectedBufSize(const SerializationContext &ctx) const {
|
||||
auto size = bitsery::details::ContainerTraits<TContainer>::size(src);
|
||||
auto size = bitsery::traits::ContainerTraits<TContainer>::size(src);
|
||||
return ctx.containerSizeSerializedBytesCount(size) + size * sizeof(TValue);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,8 +28,6 @@ using namespace testing;
|
||||
|
||||
using bitsery::ext::Growable;
|
||||
|
||||
using Buffer = typename bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct DataV1 {
|
||||
int32_t v1;
|
||||
};
|
||||
@@ -47,7 +45,7 @@ struct DataV3 {
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
ctx.createSerializer().ext(int8_t{}, Growable{}, [] (int8_t& v) { });
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(0));
|
||||
ctx.bw->flush();
|
||||
@@ -55,8 +53,8 @@ TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSessionsDataOffset) {
|
||||
SerializationContext ctx;
|
||||
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSessionsDataOffset) {
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
|
||||
|
||||
constexpr size_t DATA_SIZE = 4;
|
||||
@@ -66,7 +64,7 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSess
|
||||
ser.ext(data, Growable{}, [&ser](int32_t & v) { ser.value4b(v);});
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(3 + DATA_SIZE));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1+4 + DATA_SIZE));
|
||||
|
||||
//read value back
|
||||
auto& br = *(ctx.br);
|
||||
@@ -77,16 +75,16 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSess
|
||||
bitsery::details::readSize(br, sessionEnd, 1000000u);
|
||||
EXPECT_THAT(sessionEnd, Eq(DATA_SIZE));
|
||||
//this is the the offset from the end of buffer where actual data ends
|
||||
uint16_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<2>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+2));//1byte for session info, 2 bytes for session offset variable
|
||||
auto range = ctx.bw->getWrittenRange();
|
||||
auto dSize = std::distance(range.begin(), std::next(range.end(), -sessionsOffset));
|
||||
uint32_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<4>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+4));//1byte for session info, 4 bytes for session offset variable
|
||||
auto writtenSize = ctx.bw->getWrittenBytesCount();
|
||||
auto dSize = writtenSize - sessionsOffset;
|
||||
EXPECT_THAT(dSize, Eq(DATA_SIZE));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{19457,846, 498418};
|
||||
ctx.createSerializer();
|
||||
ctx.bw->beginSession();
|
||||
@@ -117,29 +115,8 @@ TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
|
||||
EXPECT_THAT(sessionEnd[2], Eq(12));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, WhenSessionsDataIsMoreThan0x7FFFThenWrite4BytesForSessionsOffset) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer();
|
||||
//create more sessions that can fit in 2 bytes
|
||||
for (auto i = 0u; i < 0x8000; ++i) {
|
||||
ctx.bw->beginSession();
|
||||
ctx.bw->endSession();
|
||||
}
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(0x8000+4));
|
||||
uint8_t tmp{};
|
||||
for (auto i = 0u; i < 0x8000; ++i) {
|
||||
ctx.br->beginSession();
|
||||
ctx.br->endSession();
|
||||
}
|
||||
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
ctx.br->readBytes<1>(tmp);
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -165,7 +142,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -192,7 +169,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -220,7 +197,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -256,7 +233,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -296,7 +273,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -352,7 +329,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -414,7 +391,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionsStartsAtEndOfSerialization) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
#ifndef BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
#define BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <memory>
|
||||
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
|
||||
/*
|
||||
* define some types for testing
|
||||
@@ -85,31 +86,41 @@ void serialize(S&s, MyStruct2& o) {
|
||||
s.object(o.s1);
|
||||
}
|
||||
|
||||
struct SessionsEnabledConfig: public bitsery::DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
class SerializationContext {
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::BasicWriter<bitsery::DefaultConfig, OutputAdapter>;
|
||||
using Reader = bitsery::BasicReader<bitsery::DefaultConfig, InputAdapter>;
|
||||
|
||||
|
||||
template <typename Config = bitsery::DefaultConfig>
|
||||
class BasicSerializationContext {
|
||||
public:
|
||||
bitsery::DefaultConfig::BufferType buf{};
|
||||
std::unique_ptr<bitsery::BufferWriter> bw;
|
||||
std::unique_ptr<bitsery::BufferReader> br;
|
||||
std::unique_ptr<bitsery::BasicSerializer<bitsery::DefaultConfig, true>> sbp;
|
||||
Buffer buf;
|
||||
std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>> bw;
|
||||
std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>> br;
|
||||
std::unique_ptr<bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>> sbp;
|
||||
|
||||
bitsery::Serializer createSerializer() {
|
||||
bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, false> createSerializer() {
|
||||
//make_unique is not in c++11
|
||||
bw = std::unique_ptr<bitsery::BufferWriter>(new bitsery::BufferWriter(buf));
|
||||
return bitsery::Serializer{*bw};
|
||||
bw = std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>>(new bitsery::BasicWriter<Config, OutputAdapter>(buf));
|
||||
return bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, false>{*bw};
|
||||
};
|
||||
|
||||
bitsery::BasicSerializer<bitsery::DefaultConfig, true>& createBPEnabledSerializer() {
|
||||
bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>& createBPEnabledSerializer() {
|
||||
//make_unique is not in c++11
|
||||
bw = std::unique_ptr<bitsery::BufferWriter>(new bitsery::BufferWriter(buf));
|
||||
sbp = std::unique_ptr<bitsery::BasicSerializer<bitsery::DefaultConfig, true>>(
|
||||
new bitsery::BasicSerializer<bitsery::DefaultConfig, true>{*bw});
|
||||
bw = std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>>(new bitsery::BasicWriter<Config, OutputAdapter>(buf));
|
||||
sbp = std::unique_ptr<bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>>(
|
||||
new bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>{*bw});
|
||||
return *sbp;
|
||||
};
|
||||
|
||||
size_t getBufferSize() const {
|
||||
auto range = bw->getWrittenRange();
|
||||
return std::distance(range.begin(), range.end());
|
||||
return bw->getWrittenBytesCount();
|
||||
}
|
||||
|
||||
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
|
||||
@@ -122,19 +133,24 @@ public:
|
||||
return 4;
|
||||
}
|
||||
|
||||
bitsery::Deserializer createDeserializer() {
|
||||
bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, false> createDeserializer() {
|
||||
bw->flush();
|
||||
//make_unique is not in c++11
|
||||
br = std::unique_ptr<bitsery::BufferReader>(new bitsery::BufferReader(bw->getWrittenRange()));
|
||||
return bitsery::Deserializer{*br};
|
||||
br = std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>>(
|
||||
new bitsery::BasicReader<Config, InputAdapter>(InputAdapter{buf.begin(), bw->getWrittenBytesCount()}));
|
||||
return bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, false>{*br};
|
||||
};
|
||||
|
||||
bitsery::BasicDeserializer<bitsery::DefaultConfig, true> createBPEnabledDeserializer() {
|
||||
bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, true> createBPEnabledDeserializer() {
|
||||
sbp.reset(nullptr);
|
||||
//make_unique is not in c++11
|
||||
br = std::unique_ptr<bitsery::BufferReader>(new bitsery::BufferReader(bw->getWrittenRange()));
|
||||
return bitsery::BasicDeserializer<bitsery::DefaultConfig, true>{*br};
|
||||
br = std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>>(
|
||||
new bitsery::BasicReader<Config, InputAdapter>(InputAdapter{buf.begin(), bw->getWrittenBytesCount()}));
|
||||
return bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, true>{*br};
|
||||
};
|
||||
};
|
||||
|
||||
//helper type
|
||||
using SerializationContext = BasicSerializationContext<bitsery::DefaultConfig>;
|
||||
|
||||
#endif //BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
|
||||
Reference in New Issue
Block a user