mirror of
https://github.com/fraillt/bitsery.git
synced 2026-09-17 15:54:30 +00:00
simplified usage by merging adapter writer/reader with input/output
adapter and ability to disable checks on deserialization
This commit is contained in:
committed by
Mindaugas Vinkelis
parent
1822796f2e
commit
105aa5f9e5
31
CHANGELOG.md
31
CHANGELOG.md
@@ -5,42 +5,47 @@
|
||||
* align from serializer/deserializer
|
||||
* AdapterAccess class
|
||||
* helper class Serializer/Deserializer
|
||||
* setError renamed to error
|
||||
* deprecated registerBasesList from PolymorphicContext
|
||||
* removed internal context from config, because it doesn't actually solve any problems, only allows to do same thing in multiple ways
|
||||
* internal context from config, because it doesn't actually solve any problems, only allows to do same thing in multiple ways
|
||||
* AdapterWriter/Reader classes, and their functionality is moved to `adapters`.
|
||||
* UnsafeInputBufferAdapter, instead config option is provided to disable buffer read errors
|
||||
* isValidState from stream output adapter, because it didn't provide any additional information that couldn't be queried directly on stream object.
|
||||
|
||||
## other breaking changes
|
||||
|
||||
* changed signature to all lambda methods, instead of accepting (T&) as only parameter, now accept (S& ,T& )
|
||||
since it is no longer needed to store serializer/deserializer reference, this allows
|
||||
to pass functors, function pointers or stateless lambdas
|
||||
* BufferedSessions reworked, it was removed from core bitsery functionality, and instead ability to change read/write position was added for buffered adapter
|
||||
* Growable extension now uses adapter reader/writer to directly change read/write position
|
||||
* BufferedSessions reworked, it was removed from core bitsery functionality, and instead was added ability to change read/write position directly for buffered adapters
|
||||
* if context is defined, in serializer/deserializer, it is passed as first argument by reference (instead of pointer). Other parameters are forwarded to input/output adapter.
|
||||
* for adapters save first error that occured, and ignore all the others
|
||||
* serializer/deserializer no longer owns contexts, it only have reference to adapter reader/writer
|
||||
* adapter is now owned by adapter reader/writer
|
||||
* context can no longer be null, and instead reference to context is stored in adapter reader/writer.
|
||||
* context<T> will never return nullptr
|
||||
* if context is optional contextOrNull<T> return null in case context is not defined
|
||||
* setError for input adapters renamed to error
|
||||
* context can no longer be null, and instead reference to context is stored in serializer/deserializer.
|
||||
* context<T> returns reference instead of pointer
|
||||
* if context is optional contextOrNull<T> return nullptr in case context is not defined
|
||||
* context<T> and contextOrNull<T> also check if type is convertible, so it can work with base classes
|
||||
(e.g. you can require base class of context in extension, but provided child implementation instead)
|
||||
* renamed NetworkEndianness to Endianness in config
|
||||
* MeasureSize adapter moved to separate file `/adapter/measure_size.h`
|
||||
|
||||
## improvements
|
||||
|
||||
* added quickSerialization/Deserialization overloads that can accept context as first parameter.
|
||||
* added support for custom allocator(s) for pointer like objects. More information on how to correctly use custom allocation and pointers in general see [this](doc/design/pointers.md).
|
||||
* inheritance context now accepts allocator
|
||||
* added tests for BasicMeasureSize
|
||||
* helper classes (FtorExtValue, FtorExtObject) for extensions, that reduce boilerplate when writing lambdas that accept serializer/deserializer and data.
|
||||
* helper classes (FtorExtValue, FtorExtObject) to reduce boilerplate and improve readability in places where you need to provide (des)serialize function/lambda that uses extension.
|
||||
e.g. instead of writing `s.container(obj, [](S& s, MyData& data) {s.ext(data, MyExtension{});});` you can write `s.container(obj, FtorExtObject<MyExtension>{});`
|
||||
* added config options to enable/disable checking input adapter and data errors (`CheckAdapterErrors` and `CheckDataErrors`) (default is enabled)
|
||||
|
||||
## bugfix
|
||||
* fixed enabledBitPacking where writer and internal context states was not restored properly after exiting from this function
|
||||
|
||||
## todo
|
||||
* write tests for inheritance context with allocator
|
||||
* add allocator support for polymorphic and pointer linking contexts
|
||||
* flexible syntax, enable option (using config) to use compactvalue for fundamental types by default (it should be off to preserve ABI breaking change).
|
||||
* simplify overall usage by making adapterwriter/reader base class for adapters using CRTP.
|
||||
|
||||
* improve SmartPtr by allocating shared_ptr control block using provided allocator
|
||||
* rename "flexible" to "brief_syntax"
|
||||
|
||||
# [4.6.1](https://github.com/fraillt/bitsery/compare/v4.6.0...v4.6.1) (2019-06-27)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ All cross-platform requirements are enforced at compile time, so serialized data
|
||||
* smart and raw pointers with customizable runtime polymorphism support.
|
||||
* fine-grained bit-level serialization control.
|
||||
* Easily extendable for any type.
|
||||
* Allows flexible or/and verbose syntax for better serialization control.
|
||||
* Allows brief or/and verbose syntax for better serialization control.
|
||||
* Configurable endianness support.
|
||||
* No macros.
|
||||
|
||||
@@ -61,7 +61,7 @@ void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
};
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
@@ -75,9 +75,9 @@ int main() {
|
||||
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(data, buffer);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = quickDeserialization<InputAdapter>(res, buffer.begin(), writtenSize);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
|
||||
@@ -16,14 +16,13 @@ Library design:
|
||||
|
||||
Core Serializer/Deserializer functions (alphabetical order):
|
||||
* `operator()` (4.6.1) (when flexible syntax is enabled)
|
||||
* `align` (1.0.0)
|
||||
* `adapter` (5.0.0)
|
||||
* `archive` (4.0.0) (when flexible syntax is enabled)
|
||||
* `boolValue` (4.0.0)
|
||||
* `container` (1.0.0)
|
||||
* `ext` (2.0.0)
|
||||
* `context` (3.0.0)
|
||||
* `context<T>` (4.1.0)
|
||||
* `contextOrNull<T>` (4.2.0)
|
||||
* `enableBitPacking` (4.0.0)
|
||||
* `ext` (2.0.0)
|
||||
* `object` (1.0.0)
|
||||
* `text` (1.0.0)
|
||||
* `value` (1.0.0)
|
||||
@@ -50,27 +49,23 @@ Serializer/Deserializer extensions via `ext` method (alphabetical order):
|
||||
* `ValueRange` (3.0.0)
|
||||
* `VirtualBaseClass` (4.2.0)
|
||||
|
||||
AdapterWriter/Reader functions:
|
||||
* `writeBits/readBits`
|
||||
* `writeBytes/readBytes`
|
||||
* `writeBuffer/readBuffer`
|
||||
* `align`
|
||||
* `beginSession/endSession`
|
||||
* `flush (writer only)`
|
||||
* `writtenBytesCount (writer only)`
|
||||
* `setError (reader only)`
|
||||
* `getError (reader only)`
|
||||
* `isCompletedSuccessfully (reader only)`
|
||||
|
||||
Input adapters (buffer and stream) functions:
|
||||
* `read`
|
||||
* `error`
|
||||
* `setError`
|
||||
* `align`
|
||||
* `readBits`
|
||||
* `readBytes`
|
||||
* `readBuffer`
|
||||
* `currentReadPos (get/set)` (buffer adapter only)
|
||||
* `currentReadEndPos (get/set)` (buffer adapter only)
|
||||
* `error (get/set)`
|
||||
* `isCompletedSuccessfully`
|
||||
|
||||
Output adapters (buffer and stream) functions:
|
||||
* `write`
|
||||
* `align`
|
||||
* `writeBits`
|
||||
* `writeBytes`
|
||||
* `writeBuffer`
|
||||
* `flush`
|
||||
* `currentyWritePos (get/set)` (buffer adapter only)
|
||||
* `writtenBytesCount` (buffer adapter only)
|
||||
|
||||
|
||||
|
||||
@@ -76,9 +76,9 @@ Create buffer and use helper functions for serialization and deserialization.
|
||||
```cpp
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(data, buffer);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = quickDeserialization<InputAdapter>(res, buffer.begin(), writtenSize);
|
||||
```
|
||||
|
||||
These helper functions use default configuration *bitsery::DefaultConfig*
|
||||
@@ -118,9 +118,9 @@ int main() {
|
||||
MyStruct res{};
|
||||
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(data, buffer);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
auto state = quickDeserialization<InputAdapter>(res, buffer.begin(), writtenSize);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
|
||||
|
||||
@@ -68,9 +68,9 @@ using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using namespace bitsery;
|
||||
// define Writer and Reader types,
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, Context>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, Context>;
|
||||
// define adapter types,
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -90,18 +90,10 @@ int main() {
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//create adapter writer with context
|
||||
//context is passed by reference without taking ownership
|
||||
Writer writer{buffer, ctx};
|
||||
//serialize data
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.object(data);
|
||||
writer.flush();
|
||||
auto writtenSize = quickSerialization(ctx, OutputAdapter{buffer}, data);
|
||||
|
||||
MyTypes::GameState res{};
|
||||
Reader reader {{buffer.begin(), writer.writtenBytesCount()}, ctx};
|
||||
BasicDeserializer<Reader> des {reader };
|
||||
des.object(res);
|
||||
auto state = quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ void serialize(S& s, MyStruct& o) {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//buffered stream adapter allows for faster writes
|
||||
using Writer = AdapterWriter<OutputBufferedStreamAdapter, DefaultConfig>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, 0.045};
|
||||
@@ -38,11 +35,10 @@ int main() {
|
||||
}
|
||||
|
||||
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
|
||||
Writer writer{s};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
BasicSerializer<OutputBufferedStreamAdapter> ser{s};
|
||||
ser.object(data);
|
||||
//flush to writer
|
||||
writer.flush();
|
||||
ser.adapter().flush();
|
||||
s.close();
|
||||
//reopen for reading
|
||||
|
||||
|
||||
@@ -83,8 +83,8 @@ using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, ext::InheritanceContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, ext::InheritanceContext>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -96,19 +96,12 @@ int main() {
|
||||
Buffer buf{};
|
||||
|
||||
ext::InheritanceContext ctx1;
|
||||
Writer writer{buf, ctx1};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.object(data);
|
||||
writer.flush();
|
||||
|
||||
auto writtenSize = quickSerialization(ctx1, Writer{buf}, data);
|
||||
assert(writtenSize == 4);//base is serialized once, because it is inherited virtually
|
||||
|
||||
MultipleInheritance res{0};
|
||||
ext::InheritanceContext ctx2;
|
||||
Reader reader{{buf.begin(), writer.writtenBytesCount()}, ctx2};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
des.object(res);
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
|
||||
auto state = quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
|
||||
assert(writer.writtenBytesCount() == 4);//base is serialized once, because it is inherited virtually
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -44,23 +44,21 @@ int main() {
|
||||
data.emplace_back(145.4f, 84.48f);
|
||||
std::vector<MyData> res{};
|
||||
|
||||
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
|
||||
//create buffer
|
||||
Buffer buffer{};
|
||||
|
||||
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
|
||||
//create writer and serialize container
|
||||
Writer writer{buffer};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
BasicSerializer<Writer> ser{buffer};
|
||||
ser.container(data, 10);
|
||||
writer.flush();
|
||||
ser.adapter().flush();
|
||||
|
||||
//create reader and deserialize container
|
||||
Reader reader{{buffer.begin(), writer.writtenBytesCount()}};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
BasicDeserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
|
||||
des.container(res, 10);
|
||||
|
||||
//check if everything went ok
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
assert(res == data);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,16 +82,14 @@ using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
|
||||
//we will need PointerLinkingContext to work with pointers
|
||||
//if we would require additional context for our own custom flow, we can define it as tuple like this:
|
||||
// std::tuple<MyContext,ext::PointerLinkingContext>
|
||||
//and other code will work as expected as long as it cast to proper type.
|
||||
//see context_usage.cpp for usage example
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, ext::PointerLinkingContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, ext::PointerLinkingContext>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -114,15 +112,10 @@ int main() {
|
||||
//create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
//in order to use pointers, we need to pass pointer linking context to writer/reader
|
||||
//in order to use pointers, we need to pass pointer linking context serializer/deserializer
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
Writer writer{buffer, ctx};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
//serialize our data
|
||||
ser.object(data);
|
||||
writer.flush();
|
||||
writtenSize = writer.writtenBytesCount();
|
||||
writtenSize = quickSerialization(ctx, Writer{buffer}, data);
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
//this ensures that all non-owning pointers points to data that has been serialized,
|
||||
@@ -133,13 +126,9 @@ int main() {
|
||||
Test1Data res{};
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
//pass lining context to reader
|
||||
Reader reader{{buffer.begin(), writtenSize}, ctx};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
//deserialize our data
|
||||
des.object(res);
|
||||
auto state = quickDeserialization(ctx, Reader{buffer.begin(), writtenSize}, res);
|
||||
//check if everything went find
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
|
||||
@@ -187,8 +187,8 @@ using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using Writer = OutputBufferAdapter<Buffer>;
|
||||
using Reader = InputBufferAdapter<Buffer>;
|
||||
|
||||
//we need to define few things in order to work with polymorphism
|
||||
//1) we need pointer linking context to work with pointers
|
||||
@@ -196,10 +196,8 @@ using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<ext::StandardRTTI>>;
|
||||
//NOTE:
|
||||
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
|
||||
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, TContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, TContext>;
|
||||
|
||||
using Serializer = BasicSerializer<Writer, TContext>;
|
||||
using Deserializer = BasicDeserializer<Reader, TContext>;
|
||||
|
||||
//checks if deserialized data is equal
|
||||
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
|
||||
@@ -232,6 +230,7 @@ int main() {
|
||||
//create buffer to store data
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
// we will not use quickSerialization/Deserialization functions to show, that we need to register polymorphic classes, explicitly
|
||||
{
|
||||
|
||||
//STEP 2
|
||||
@@ -239,13 +238,12 @@ int main() {
|
||||
// bind it with base polymorphic types, it will go through all reachable classes that is defined in first step.
|
||||
// NOTE: you dont need to add Rectangle to reach for RoundedRectangle
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<BasicSerializer<Writer>>(MyPolymorphicClassesForRegistering{});
|
||||
std::get<1>(ctx).registerBasesList<Serializer>(MyPolymorphicClassesForRegistering{});
|
||||
//create writer and serialize
|
||||
Writer writer{buffer, ctx};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
Serializer ser{ctx, buffer};
|
||||
ser.object(data);
|
||||
writer.flush();
|
||||
writtenSize = writer.writtenBytesCount();
|
||||
ser.adapter().flush();
|
||||
writtenSize = ser.adapter().writtenBytesCount();
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
//this ensures that all non-owning pointers points to data that has been serialized,
|
||||
@@ -255,13 +253,11 @@ int main() {
|
||||
SomeShapes res{};
|
||||
{
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<BasicDeserializer<Reader>>(MyPolymorphicClassesForRegistering{});
|
||||
//serialize our data
|
||||
Reader reader {{buffer.begin(), writtenSize}, ctx};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
std::get<1>(ctx).registerBasesList<Deserializer>(MyPolymorphicClassesForRegistering{});
|
||||
//deserialize our data
|
||||
Deserializer des{ctx, buffer.begin(), writtenSize};
|
||||
des.object(res);
|
||||
//check if everything went find
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
// clear shared state from pointer linking context,
|
||||
|
||||
@@ -33,7 +33,12 @@ namespace bitsery {
|
||||
class BufferIterators {
|
||||
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
|
||||
using BuffNonConst = typename std::remove_const<Buffer>::type;
|
||||
|
||||
public:
|
||||
BufferIterators(const BufferIterators&) = delete;
|
||||
BufferIterators& operator=(const BufferIterators&) = delete;
|
||||
BufferIterators(BufferIterators&&) = default;
|
||||
BufferIterators& operator=(BufferIterators&&) = default;
|
||||
virtual ~BufferIterators() = default;
|
||||
protected:
|
||||
|
||||
using TIterator = typename std::conditional<isConstBuffer,
|
||||
@@ -52,9 +57,12 @@ namespace bitsery {
|
||||
TIterator endIt;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class InputBufferAdapter : public BufferIterators<Buffer> {
|
||||
template<typename Buffer, typename Config = DefaultConfig>
|
||||
class InputBufferAdapter: public BufferIterators<Buffer>,
|
||||
public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
|
||||
public:
|
||||
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>>;
|
||||
using TConfig = Config;
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
@@ -73,27 +81,14 @@ namespace bitsery {
|
||||
_endReadPos{std::next(begin, size)} {
|
||||
}
|
||||
|
||||
void read(TValue *data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, _endReadPos) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
if (_overflowOnReadEndPos)
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
InputBufferAdapter(const InputBufferAdapter&) = delete;
|
||||
InputBufferAdapter& operator=(const InputBufferAdapter&) = delete;
|
||||
|
||||
InputBufferAdapter(InputBufferAdapter&&) = default;
|
||||
InputBufferAdapter& operator = (InputBufferAdapter&&) = default;
|
||||
|
||||
void currentReadPos(size_t pos) {
|
||||
if (static_cast<size_t>(std::distance(this->beginIt, this->endIt)) >= pos) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
currentReadPosChecked(pos, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
@@ -101,6 +96,8 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
// assert that CheckAdapterErrors is enabled, otherwise it will simply will not work even if data and buffer is not corrupted
|
||||
static_assert(Config::CheckAdapterErrors, "Please enable CheckAdapterErrors to use this functionality.");
|
||||
const auto buffSize = static_cast<size_t>(std::distance(this->beginIt, this->endIt));
|
||||
if (buffSize >= pos) {
|
||||
_overflowOnReadEndPos = pos == 0;
|
||||
@@ -134,32 +131,10 @@ namespace bitsery {
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt && _err == ReaderError::NoError;
|
||||
}
|
||||
|
||||
private:
|
||||
TIterator _endReadPos;
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
bool _overflowOnReadEndPos = true;
|
||||
};
|
||||
|
||||
// this adapter ignore all errors, it is undefined behaviour when error happens
|
||||
template<typename Buffer>
|
||||
class UnsafeInputBufferAdapter : public BufferIterators<Buffer> {
|
||||
public:
|
||||
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
UnsafeInputBufferAdapter(TIterator beginIt, TIterator endIt) : BufferIterators<Buffer>(beginIt, endIt) {
|
||||
}
|
||||
|
||||
UnsafeInputBufferAdapter(TIterator begin, size_t size)
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
|
||||
}
|
||||
|
||||
void read(TValue *data, size_t size) {
|
||||
void readChecked(TValue *data, size_t size, std::false_type) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
@@ -167,48 +142,47 @@ namespace bitsery {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
}
|
||||
|
||||
void currentReadPos(size_t pos) {
|
||||
if (std::distance(this->beginIt, this->endIt) >= pos) {
|
||||
void readChecked(TValue *data, size_t size, std::true_type) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, _endReadPos) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
if (_overflowOnReadEndPos)
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
void readInternal(TValue *data, size_t size) {
|
||||
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void currentReadPosChecked(size_t pos, std::true_type) {
|
||||
if (static_cast<size_t>(std::distance(this->beginIt, this->endIt)) >= pos) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return static_cast<size_t>(std::distance(this->beginIt, this->posIt));
|
||||
void currentReadPosChecked(size_t pos, std::false_type) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t) {
|
||||
static_assert(std::is_void<Buffer>::value, "`currentReadEndPos(size_t)` is not supported with UnsafeInputBufferAdapter");
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
return static_cast<size_t>(std::distance(this->beginIt, this->endIt));
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _err;
|
||||
}
|
||||
|
||||
void error(ReaderError error) {
|
||||
if (_err == ReaderError::NoError) {
|
||||
_err = error;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt && _err == ReaderError::NoError;
|
||||
}
|
||||
|
||||
private:
|
||||
TIterator _endReadPos;
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
bool _overflowOnReadEndPos = true;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class OutputBufferAdapter {
|
||||
template<typename Buffer, typename Config = DefaultConfig>
|
||||
class OutputBufferAdapter: public details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>> {
|
||||
public:
|
||||
|
||||
friend details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>>;
|
||||
using TConfig = Config;
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
|
||||
@@ -223,9 +197,10 @@ namespace bitsery {
|
||||
init(TResizable{});
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
OutputBufferAdapter(const OutputBufferAdapter&) = delete;
|
||||
OutputBufferAdapter& operator=(const OutputBufferAdapter&) = delete;
|
||||
OutputBufferAdapter(OutputBufferAdapter&&) = default;
|
||||
OutputBufferAdapter& operator = (OutputBufferAdapter&&) = default;
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
const auto currPos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
@@ -252,7 +227,11 @@ namespace bitsery {
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
|
||||
|
||||
Buffer *_buffer;
|
||||
void writeInternal(const TValue *data, size_t size) {
|
||||
writeInternalImpl(data, size, TResizable{});
|
||||
}
|
||||
|
||||
Buffer* _buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
size_t _biggestCurrentPos{};
|
||||
@@ -270,7 +249,7 @@ namespace bitsery {
|
||||
_outIt = std::begin(*_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, const size_t size, std::true_type) {
|
||||
void writeInternalImpl(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
|
||||
@@ -297,7 +276,7 @@ namespace bitsery {
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::next(std::begin(*_buffer), pos);
|
||||
|
||||
writeInternal(data, size, std::true_type{});
|
||||
writeInternalImpl(data, size, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +298,7 @@ namespace bitsery {
|
||||
_end = std::end(*_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::false_type) {
|
||||
void writeInternalImpl(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
|
||||
95
include/bitsery/adapter/measure_size.h
Normal file
95
include/bitsery/adapter/measure_size.h
Normal file
@@ -0,0 +1,95 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2019 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_ADAPTER_MEASURE_SIZE_H
|
||||
#define BITSERY_ADAPTER_MEASURE_SIZE_H
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename Config>
|
||||
class BasicMeasureSize {
|
||||
public:
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = Config;
|
||||
using TValue = void;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T&) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_currPosBits += details::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T*, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_currPosBits += details::BitsSize<T>::value * count;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T&, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(bitsCount <= details::BitsSize<T>::value);
|
||||
_currPosBits += bitsCount;
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
align();
|
||||
const auto newPos = pos * 8;
|
||||
if (_currPosBits > newPos)
|
||||
_prevLargestPos = _currPosBits;
|
||||
_currPosBits = newPos;
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return _currPosBits / 8;
|
||||
}
|
||||
|
||||
void align() {
|
||||
auto _scratch = (_currPosBits % 8);
|
||||
_currPosBits += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t writtenBytesCount() const {
|
||||
const auto max = _currPosBits > _prevLargestPos ? _currPosBits : _prevLargestPos;
|
||||
return max / 8;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _prevLargestPos{};
|
||||
size_t _currPosBits{};
|
||||
};
|
||||
|
||||
//helper type for default config
|
||||
using MeasureSize = BasicMeasureSize<DefaultConfig>;
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_MEASURE_SIZE_H
|
||||
@@ -29,25 +29,21 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicInputStreamAdapter {
|
||||
template <typename TChar, typename Config, typename CharTraits>
|
||||
class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>> {
|
||||
public:
|
||||
friend details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
using TConfig = Config;
|
||||
using TValue = TChar;
|
||||
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
:_ios{std::addressof(istream)} {}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn( data , size )) != _zeroIfNoErrors) {
|
||||
*data = {};
|
||||
if (_zeroIfNoErrors == 0) {
|
||||
error(_ios->rdstate() == std::ios_base::badbit
|
||||
? ReaderError::ReadingError
|
||||
: ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
BasicInputStreamAdapter(const BasicInputStreamAdapter&) = delete;
|
||||
BasicInputStreamAdapter& operator = (const BasicInputStreamAdapter&) = delete;
|
||||
|
||||
}
|
||||
BasicInputStreamAdapter(BasicInputStreamAdapter&&) = default;
|
||||
BasicInputStreamAdapter& operator = (BasicInputStreamAdapter&&) = default;
|
||||
|
||||
void currentReadPos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
|
||||
@@ -86,24 +82,41 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void readInternal(TValue* data, size_t size) {
|
||||
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void readChecked(TValue* data, size_t size, std::true_type) {
|
||||
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(data, size)) != _zeroIfNoErrors) {
|
||||
*data = {};
|
||||
if (_zeroIfNoErrors == 0) {
|
||||
error(_ios->rdstate() == std::ios_base::badbit
|
||||
? ReaderError::ReadingError
|
||||
: ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void readChecked(TValue* data, size_t size, std::false_type) {
|
||||
_ios->rdbuf()->sgetn(data , size);
|
||||
}
|
||||
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
size_t _zeroIfNoErrors{};
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicOutputStreamAdapter {
|
||||
template <typename TChar, typename Config, typename CharTraits>
|
||||
class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>> {
|
||||
public:
|
||||
friend details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
|
||||
using TConfig = Config;
|
||||
using TValue = TChar;
|
||||
|
||||
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream)
|
||||
:_ios{std::addressof(ostream)} {}
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
void currentWritePos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
}
|
||||
@@ -124,18 +137,22 @@ namespace bitsery {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
//this method is only for stream writing
|
||||
bool isValidState() const {
|
||||
return !_ios->bad();
|
||||
private:
|
||||
|
||||
void writeInternal(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
|
||||
class BasicBufferedOutputStreamAdapter {
|
||||
template <typename TChar, typename Config, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
|
||||
class BasicBufferedOutputStreamAdapter:
|
||||
public details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>> {
|
||||
public:
|
||||
friend details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
|
||||
using TConfig = Config;
|
||||
using Buffer = TBuffer;
|
||||
using BufferIt = typename traits::BufferAdapterTraits<TBuffer>::TIterator;
|
||||
static_assert(details::IsDefined<BufferIt>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer for BasicBufferedOutputStreamAdapter");
|
||||
@@ -144,19 +161,19 @@ namespace bitsery {
|
||||
|
||||
//bufferSize is used when buffer is dynamically allocated
|
||||
BasicBufferedOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream, size_t bufferSize = 256)
|
||||
:_adapter(ostream),
|
||||
:_ios(std::addressof(ostream)),
|
||||
_buf{},
|
||||
_outIt{}
|
||||
{
|
||||
init(bufferSize, TResizable{});
|
||||
}
|
||||
|
||||
//we need to explicitly declare move logic, in case buffer is static, because after move it will be invalidated
|
||||
//we need to explicitly declare move logic, because after move buffer might be invalidated
|
||||
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) = delete;
|
||||
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
|
||||
|
||||
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
|
||||
: _adapter{std::move(rhs._adapter)},
|
||||
: _ios{rhs._ios},
|
||||
_buf{},
|
||||
_outIt{}
|
||||
{
|
||||
@@ -166,7 +183,7 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
|
||||
_adapter = std::move(rhs._adapter);
|
||||
_ios = rhs._ios;
|
||||
//get current written size, before move
|
||||
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
|
||||
_buf = std::move(rhs._buf);
|
||||
@@ -174,29 +191,6 @@ namespace bitsery {
|
||||
return *this;
|
||||
};
|
||||
|
||||
~BasicBufferedOutputStreamAdapter() = default;
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
#else
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
#endif
|
||||
} else {
|
||||
//when buffer is full write out to stream
|
||||
_outIt = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
|
||||
_adapter.write(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void currentWritePos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
}
|
||||
@@ -208,23 +202,48 @@ namespace bitsery {
|
||||
|
||||
void flush() {
|
||||
auto begin = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
|
||||
writeToStream(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
|
||||
_outIt = begin;
|
||||
_adapter.flush();
|
||||
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
|
||||
ostream->flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _adapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
//this method is only for stream writing
|
||||
bool isValidState() const {
|
||||
return _adapter.isValidState();
|
||||
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
|
||||
//streaming doesn't return written bytes
|
||||
return 0u;
|
||||
}
|
||||
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
|
||||
|
||||
void writeInternal(const TValue* data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
}
|
||||
#else
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
//when buffer is full write out to stream
|
||||
_outIt = std::begin(_buf);
|
||||
writeToStream(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
|
||||
writeToStream(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void writeToStream(const TValue* data, size_t size) {
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
void init (size_t bufferSize, std::true_type) {
|
||||
_buf.resize(bufferSize);
|
||||
_outIt = std::begin(_buf);
|
||||
@@ -233,30 +252,30 @@ namespace bitsery {
|
||||
_outIt = std::begin(_buf);
|
||||
}
|
||||
|
||||
BasicOutputStreamAdapter<TChar, CharTraits> _adapter;
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
TBuffer _buf;
|
||||
BufferIt _outIt;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, CharTraits>, public BasicOutputStreamAdapter<TChar, CharTraits> {
|
||||
template <typename TChar, typename Config, typename CharTraits>
|
||||
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, Config, CharTraits>, public BasicOutputStreamAdapter<TChar, Config, CharTraits> {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
|
||||
//both bases contain reference to same iostream, so no need to do anything
|
||||
BasicIOStreamAdapter(std::basic_ios<TChar, CharTraits>& iostream)
|
||||
:BasicInputStreamAdapter<TChar, CharTraits>{iostream},
|
||||
BasicOutputStreamAdapter<TChar, CharTraits>{iostream} {
|
||||
:BasicInputStreamAdapter<TChar, Config, CharTraits>{iostream},
|
||||
BasicOutputStreamAdapter<TChar, Config, CharTraits>{iostream} {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//helper types for most common implementations for std streams
|
||||
using OutputStreamAdapter = BasicOutputStreamAdapter<char, std::char_traits<char>>;
|
||||
using InputStreamAdapter = BasicInputStreamAdapter<char, std::char_traits<char>>;
|
||||
using IOStreamAdapter = BasicIOStreamAdapter<char, std::char_traits<char>>;
|
||||
using OutputStreamAdapter = BasicOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
|
||||
using InputStreamAdapter = BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
|
||||
using IOStreamAdapter = BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
|
||||
|
||||
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, std::char_traits<char>>;
|
||||
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_STREAM_H
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
//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_ADAPTER_READER_H
|
||||
#define BITSERY_ADAPTER_READER_H
|
||||
|
||||
#include "details/adapter_common.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TReader>
|
||||
class AdapterReaderBitPackingWrapper;
|
||||
|
||||
template<typename InputAdapter, typename Config, typename Context=void>
|
||||
struct AdapterReader: public details::AdapterAndContext<InputAdapter, Config, Context> {
|
||||
|
||||
using details::AdapterAndContext<InputAdapter, Config, Context>::AdapterAndContext;
|
||||
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T& v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T* buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
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() {
|
||||
}
|
||||
|
||||
void currentReadPos(size_t pos) {
|
||||
this->_adapter.currentReadPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return this->_adapter.currentReadPos();
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
this->_adapter.currentReadEndPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
return this->_adapter.currentReadEndPos();
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->_adapter.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return this->_adapter.error();
|
||||
}
|
||||
|
||||
void error(ReaderError error) {
|
||||
this->_adapter.error(error);
|
||||
}
|
||||
|
||||
using typename details::AdapterAndContext<InputAdapter, Config, Context>::TValue;
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
this->_adapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if necessary
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *, size_t , std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TReader>
|
||||
class AdapterReaderBitPackingWrapper: public details::AdapterAndContextWrapper<TReader> {
|
||||
public:
|
||||
|
||||
using details::AdapterAndContextWrapper<TReader>::AdapterAndContextWrapper;
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
~AdapterReaderBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
this->_wrapped.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
error(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
void currentReadPos(size_t pos) {
|
||||
align();
|
||||
this->_wrapped.currentReadPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return this->_wrapped.currentReadPos();
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
this->_wrapped.currentReadEndPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
return this->_wrapped.currentReadEndPos();
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->_wrapped.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return this->_wrapped.error();
|
||||
}
|
||||
|
||||
void error(ReaderError error) {
|
||||
this->_wrapped.error(error);
|
||||
}
|
||||
|
||||
private:
|
||||
using UnsignedValue = typename std::make_unsigned<typename TReader::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
template<typename T>
|
||||
void readBitsInternal(T &v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BitsSize<UnsignedValue>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
namespace details {
|
||||
// used in "making friends" with non-wrapped deserializer type
|
||||
template <typename TReader>
|
||||
struct GetNonWrappedAdapterReader {
|
||||
using Reader = TReader;
|
||||
};
|
||||
|
||||
template <typename TWrapped>
|
||||
struct GetNonWrappedAdapterReader<AdapterReaderBitPackingWrapper<TWrapped>> {
|
||||
using Reader = TWrapped;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_READER_H
|
||||
@@ -1,346 +0,0 @@
|
||||
//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_ADAPTER_WRITER_H
|
||||
#define BITSERY_ADAPTER_WRITER_H
|
||||
|
||||
#include "details/adapter_common.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template <typename Config, typename Context=void>
|
||||
class BasicMeasureSize {
|
||||
struct NoExternalContext{};
|
||||
public:
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = Config;
|
||||
using InternalContext = typename Config::InternalContext;
|
||||
using ExternalContext = typename std::conditional<std::is_void<Context>::value, NoExternalContext, Context>::type;
|
||||
using TValue = void;
|
||||
|
||||
static_assert(details::IsSpecializationOf<InternalContext, std::tuple>::value,
|
||||
"Config::InternalContext must be std::tuple");
|
||||
|
||||
// take ownership of adapter
|
||||
template <typename T=Context, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
|
||||
explicit BasicMeasureSize()
|
||||
:_internalContext{},
|
||||
_externalContext{}
|
||||
{
|
||||
}
|
||||
|
||||
// get context by reference, do not take ownership of it
|
||||
template <typename T=Context, typename std::enable_if<!std::is_void<T>::value>::type* = nullptr>
|
||||
explicit BasicMeasureSize(ExternalContext& ctx)
|
||||
:_internalContext{},
|
||||
_externalContext{ctx}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_currPosBits += details::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_currPosBits += details::BitsSize<T>::value * count;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(bitsCount <= details::BitsSize<T>::value);
|
||||
_currPosBits += bitsCount;
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
align();
|
||||
const auto newPos = pos * 8;
|
||||
if (_currPosBits > newPos)
|
||||
_prevLargestPos = _currPosBits;
|
||||
_currPosBits = newPos;
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return _currPosBits / 8;
|
||||
}
|
||||
|
||||
void align() {
|
||||
auto _scratch = (_currPosBits % 8);
|
||||
_currPosBits += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t writtenBytesCount() const {
|
||||
const auto max = _currPosBits > _prevLargestPos ? _currPosBits : _prevLargestPos;
|
||||
return max / 8;
|
||||
}
|
||||
|
||||
ExternalContext& externalContext() {
|
||||
return _externalContext;
|
||||
}
|
||||
|
||||
InternalContext& internalContext() {
|
||||
return _internalContext;
|
||||
}
|
||||
|
||||
private:
|
||||
InternalContext _internalContext;
|
||||
typename std::conditional<std::is_void<Context>::value,
|
||||
ExternalContext,ExternalContext&>::type _externalContext;
|
||||
|
||||
private:
|
||||
size_t _prevLargestPos{};
|
||||
size_t _currPosBits{};
|
||||
};
|
||||
|
||||
//helper type for default config
|
||||
using MeasureSize = BasicMeasureSize<DefaultConfig>;
|
||||
|
||||
template <typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
template<typename OutputAdapter, typename Config, typename Context=void>
|
||||
struct AdapterWriter: public details::AdapterAndContext<OutputAdapter, Config, Context> {
|
||||
|
||||
using details::AdapterAndContext<OutputAdapter, Config, Context>::AdapterAndContext;
|
||||
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
using typename details::AdapterAndContext<OutputAdapter, Config, Context>::TValue;
|
||||
|
||||
~AdapterWriter() {
|
||||
flush();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(&v, 1);
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
this->_adapter.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return this->_adapter.currentWritePos();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
this->_adapter.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return this->_adapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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);
|
||||
this->_adapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
this->_adapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper: public details::AdapterAndContextWrapper<TWriter> {
|
||||
public:
|
||||
using details::AdapterAndContextWrapper<TWriter>::AdapterAndContextWrapper;
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
~AdapterWriterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
this->_wrapped.template writeBytes<SIZE,T>(v);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
|
||||
assert(v <= (bitsCount < 64
|
||||
? (1ULL << bitsCount) - 1
|
||||
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
align();
|
||||
this->_wrapped.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return this->_wrapped.currentWritePos();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
this->_wrapped.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return this->_wrapped.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
using UnsignedType = typename std::make_unsigned<typename TWriter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedType>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value >>= valueSize;
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
//overload for TValue, for better performance
|
||||
void writeBitsInternal(const UnsignedType &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
};
|
||||
|
||||
namespace details {
|
||||
// used in "making friends" with non-wrapped serializer type
|
||||
template <typename TWriter>
|
||||
struct GetNonWrappedAdapterWriter {
|
||||
using Writer = TWriter;
|
||||
};
|
||||
|
||||
template <typename TWrapped>
|
||||
struct GetNonWrappedAdapterWriter<AdapterWriterBitPackingWrapper<TWrapped>> {
|
||||
using Writer = TWrapped;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTER_WRITER_H
|
||||
@@ -36,14 +36,15 @@ namespace bitsery {
|
||||
BigEndian
|
||||
};
|
||||
|
||||
//default configuration for buffer writing/reading operations
|
||||
// default configuration for serialization and deserialization
|
||||
struct DefaultConfig {
|
||||
//data will be stored in little endian, independant of host.
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
//list of contexts that will be instanciated internally within serializer/deserializer.
|
||||
//contexts must be default constructable.
|
||||
//internal context has priority, if external context with the same type exists.
|
||||
using InternalContext = std::tuple<>;
|
||||
// defines endianness of data that is read from input adapter and written to output adapter.
|
||||
static constexpr EndiannessType Endianness = EndiannessType::LittleEndian;
|
||||
// these flags allow to improve deserialization performance if data is trusted
|
||||
// enables/disables checks for buffer end or stream read errors in input adapter
|
||||
static constexpr bool CheckAdapterErrors = true;
|
||||
// enables/disables checks for other errors that can significantly affect performance
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,39 +25,151 @@
|
||||
#define BITSERY_DESERIALIZER_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "adapter_reader.h"
|
||||
#include "details/adapter_common.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
template<typename TAdapter>
|
||||
class InputAdapterBitPackingWrapper {
|
||||
public:
|
||||
|
||||
template<typename TAdapterReader>
|
||||
class BasicDeserializer {
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TAdapter::TConfig;
|
||||
using TValue = typename TAdapter::TValue;
|
||||
|
||||
InputAdapterBitPackingWrapper(TAdapter& adapter)
|
||||
: _wrapped{adapter}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
~InputAdapterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
this->_wrapped.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
handleAlignErrors(tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
|
||||
}
|
||||
}
|
||||
|
||||
void currentReadPos(size_t pos) {
|
||||
align();
|
||||
this->_wrapped.currentReadPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return this->_wrapped.currentReadPos();
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
this->_wrapped.currentReadEndPos(pos);
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
return this->_wrapped.currentReadEndPos();
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->_wrapped.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return this->_wrapped.error();
|
||||
}
|
||||
|
||||
void error(ReaderError error) {
|
||||
this->_wrapped.error(error);
|
||||
}
|
||||
|
||||
private:
|
||||
TAdapter& _wrapped;
|
||||
using UnsignedValue = typename std::make_unsigned<typename TAdapter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
template<typename T>
|
||||
void readBitsInternal(T &v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BitsSize<UnsignedValue>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = res;
|
||||
}
|
||||
|
||||
void handleAlignErrors(ScratchType value, std::true_type) {
|
||||
if (value)
|
||||
error(ReaderError::InvalidData);
|
||||
}
|
||||
|
||||
void handleAlignErrors(ScratchType, std::false_type) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename TInputAdapter, typename TContext = void>
|
||||
class BasicDeserializer: public details::AdapterAndContextRef<TInputAdapter, TContext> {
|
||||
public:
|
||||
//helper type, that always returns bit-packing enabled type, useful inside deserialize function when enabling bitpacking
|
||||
using BPEnabledType = BasicDeserializer<typename std::conditional<TAdapterReader::BitPackingEnabled,
|
||||
TAdapterReader,
|
||||
AdapterReaderBitPackingWrapper<TAdapterReader>>::type>;
|
||||
using BPEnabledType = BasicDeserializer<typename std::conditional<TInputAdapter::BitPackingEnabled,
|
||||
TInputAdapter,
|
||||
details::InputAdapterBitPackingWrapper<TInputAdapter>>::type, TContext>;
|
||||
|
||||
explicit BasicDeserializer(TAdapterReader& reader)
|
||||
: _reader{reader}
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
T& context() {
|
||||
return *details::getContext<true, T>(_reader.context());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull() {
|
||||
return details::getContext<false, T>(_reader.context());
|
||||
}
|
||||
using details::AdapterAndContextRef<TInputAdapter, TContext>::AdapterAndContextRef;
|
||||
|
||||
/*
|
||||
* object function
|
||||
@@ -100,7 +212,7 @@ namespace bitsery {
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
|
||||
void value(T &v) {
|
||||
using TValue = typename details::IntegralFromFundamental<T>::TValue;
|
||||
_reader.template readBytes<VSIZE>(reinterpret_cast<TValue &>(v));
|
||||
this->_adapter.template readBytes<VSIZE>(reinterpret_cast<TValue &>(v));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -108,7 +220,7 @@ namespace bitsery {
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -120,7 +232,7 @@ namespace bitsery {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
extension.deserialize(*this, _reader, obj, std::forward<Fnc>(fnc));
|
||||
extension.deserialize(*this, this->_adapter, obj, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
@@ -130,7 +242,7 @@ namespace bitsery {
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.deserialize(*this, _reader, obj, [](BasicDeserializer& s, VType &v) { s.value<VSIZE>(v);});
|
||||
extension.deserialize(*this, this->_adapter, obj, [](BasicDeserializer& s, VType &v) { s.value<VSIZE>(v);});
|
||||
}
|
||||
|
||||
template<typename T, typename Ext>
|
||||
@@ -140,14 +252,16 @@ namespace bitsery {
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.deserialize(*this, _reader, obj, [](BasicDeserializer& s, VType &v) { s.object(v); });
|
||||
extension.deserialize(*this, this->_adapter, obj, [](BasicDeserializer& s, VType &v) { s.object(v); });
|
||||
}
|
||||
|
||||
/*
|
||||
* boolValue
|
||||
*/
|
||||
void boolValue(bool &v) {
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
procBoolValue(v,
|
||||
std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{},
|
||||
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -161,7 +275,7 @@ namespace bitsery {
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&) overload without `maxSize` for static containers");
|
||||
size_t length;
|
||||
details::readSize(_reader, length, maxSize);
|
||||
readSize(length, maxSize);
|
||||
traits::ContainerTraits<T>::resize(str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
@@ -173,7 +287,7 @@ namespace bitsery {
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
size_t length;
|
||||
details::readSize(_reader, length, traits::ContainerTraits<T>::size(str));
|
||||
readSize(length, traits::ContainerTraits<T>::size(str));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
|
||||
@@ -190,7 +304,7 @@ namespace bitsery {
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
readSize(size, maxSize);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
@@ -202,7 +316,7 @@ namespace bitsery {
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
readSize(size, maxSize);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
@@ -214,7 +328,7 @@ namespace bitsery {
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
readSize(size, maxSize);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
@@ -318,7 +432,10 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
|
||||
TAdapterReader& _reader;
|
||||
void readSize(size_t& size, size_t maxSize) {
|
||||
details::readSize(this->_adapter, size, maxSize,
|
||||
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
|
||||
}
|
||||
|
||||
//process value types
|
||||
//false_type means that we must process all elements individually
|
||||
@@ -335,7 +452,7 @@ namespace bitsery {
|
||||
using TValue = typename std::decay<decltype(*first)>::type;
|
||||
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
|
||||
if (first != last)
|
||||
_reader.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), std::distance(first, last));
|
||||
this->_adapter.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), std::distance(first, last));
|
||||
}
|
||||
|
||||
//process by calling functions
|
||||
@@ -364,20 +481,26 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
|
||||
void procBoolValue(bool &v, std::true_type) {
|
||||
template <typename HandleDataErrors>
|
||||
void procBoolValue(bool &v, std::true_type, HandleDataErrors) {
|
||||
uint8_t tmp{};
|
||||
_reader.readBits(tmp, 1);
|
||||
this->_adapter.readBits(tmp, 1);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
void procBoolValue(bool &v, std::false_type) {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
void procBoolValue(bool &v, std::false_type, std::true_type) {
|
||||
uint8_t tmp{};
|
||||
this->_adapter.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.error(ReaderError::InvalidData);
|
||||
this->_adapter.error(ReaderError::InvalidData);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
void procBoolValue(bool &v, std::false_type, std::false_type) {
|
||||
uint8_t tmp{};
|
||||
this->_adapter.template readBytes<1>(tmp);
|
||||
v = tmp > 0;
|
||||
}
|
||||
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
@@ -388,11 +511,19 @@ namespace bitsery {
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
//create deserializer using bitpacking wrapper
|
||||
AdapterReaderBitPackingWrapper<TAdapterReader> bitPackingWrapper{_reader};
|
||||
BPEnabledType deserializer{bitPackingWrapper};
|
||||
fnc(deserializer);
|
||||
auto des = createWithContext(std::integral_constant<bool, BasicDeserializer::HasContext>{});
|
||||
fnc(des);
|
||||
}
|
||||
|
||||
BPEnabledType createWithContext(std::true_type) {
|
||||
return BPEnabledType{this->_context, this->_adapter};
|
||||
}
|
||||
|
||||
BPEnabledType createWithContext(std::false_type) {
|
||||
return BPEnabledType{this->_adapter};
|
||||
}
|
||||
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
void object(details::DummyType&) {
|
||||
|
||||
@@ -414,10 +545,16 @@ namespace bitsery {
|
||||
//helper function that set ups all the basic steps and after deserialziation returns status
|
||||
template <typename InputAdapter, typename T>
|
||||
std::pair<ReaderError, bool> quickDeserialization(InputAdapter adapter, T& value) {
|
||||
AdapterReader<InputAdapter, DefaultConfig> reader{std::move(adapter)};
|
||||
BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>> des{reader};
|
||||
BasicDeserializer<InputAdapter> des{std::move(adapter)};
|
||||
des.object(value);
|
||||
return {reader.error(), reader.isCompletedSuccessfully()};
|
||||
return {des.adapter().error(), des.adapter().isCompletedSuccessfully()};
|
||||
}
|
||||
|
||||
template <typename Context , typename InputAdapter, typename T>
|
||||
std::pair<ReaderError, bool> quickDeserialization(Context& ctx, InputAdapter adapter, T& value) {
|
||||
BasicDeserializer<InputAdapter, Context> des{ctx, std::move(adapter)};
|
||||
des.object(value);
|
||||
return {des.adapter().error(), des.adapter().isCompletedSuccessfully()};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,28 +30,78 @@
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
enum class ReaderError {
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData,
|
||||
InvalidPointer
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T, template<typename...> class Template>
|
||||
struct IsSpecializationOf : std::false_type {
|
||||
};
|
||||
/**
|
||||
* size read/write functions
|
||||
*/
|
||||
template <typename Reader, typename TCheckMaxSize>
|
||||
void readSize(Reader& r, size_t& size, size_t maxSize, TCheckMaxSize) {
|
||||
uint8_t hb{};
|
||||
r.template readBytes<1>(hb);
|
||||
if (hb < 0x80u) {
|
||||
size = hb;
|
||||
} else {
|
||||
uint8_t lb{};
|
||||
r.template readBytes<1>(lb);
|
||||
if (hb & 0x40u) {
|
||||
uint16_t lw{};
|
||||
r.template readBytes<2>(lw);
|
||||
size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw;
|
||||
} else {
|
||||
size = ((hb & 0x7Fu) << 8) | lb;
|
||||
}
|
||||
}
|
||||
handleReadMaxSize(r, size, maxSize, TCheckMaxSize{});
|
||||
}
|
||||
|
||||
template<template<typename...> class Template, typename... Args>
|
||||
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
|
||||
};
|
||||
template <typename Reader>
|
||||
void handleReadMaxSize(Reader& r, size_t& size, size_t maxSize, std::true_type) {
|
||||
if (size > maxSize) {
|
||||
r.error(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Reader>
|
||||
void handleReadMaxSize(Reader&, size_t&, size_t, std::false_type) {
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
|
||||
};
|
||||
template <typename Writter>
|
||||
void writeSize(Writter& w, const size_t size) {
|
||||
if (size < 0x80u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
if (size < 0x4000u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 8) | 0x80u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
assert(size < 0x40000000u);
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 24) | 0xC0u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size >> 16));
|
||||
w.template writeBytes<2>(static_cast<uint16_t>(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* swap utils
|
||||
*/
|
||||
|
||||
//add swap functions to class, to avoid compilation warning about unused functions
|
||||
struct SwapImpl {
|
||||
@@ -92,6 +142,9 @@ namespace bitsery {
|
||||
return SwapImpl::exec(static_cast<UT>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* endianness utils
|
||||
*/
|
||||
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
|
||||
struct EndiannessTestData {
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
@@ -105,6 +158,16 @@ namespace bitsery {
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
template <typename Config>
|
||||
using ShouldSwap = std::integral_constant<bool, Config::Endianness != details::getSystemEndianness()>;
|
||||
|
||||
/**
|
||||
* helper types to work with bits
|
||||
*/
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ScratchType {
|
||||
@@ -116,77 +179,122 @@ namespace bitsery {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
template<typename Adapter, typename Config, typename Context>
|
||||
class AdapterAndContext {
|
||||
struct NoContext{};
|
||||
public:
|
||||
using TConfig = Config;
|
||||
using TContext = typename std::conditional<std::is_void<Context>::value, NoContext, Context>::type;
|
||||
using TValue = typename Adapter::TValue;
|
||||
/**
|
||||
* output/input adapter base that handles endianness
|
||||
*/
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
template<typename Adapter>
|
||||
struct OutputAdapterBaseCRTP {
|
||||
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
writeSwapped(&v, 1, ShouldSwap<typename Adapter::TConfig>{});
|
||||
|
||||
// take ownership of adapter
|
||||
template <typename T=Context, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
|
||||
explicit AdapterAndContext(Adapter&& adapter)
|
||||
: _adapter{std::move(adapter)},
|
||||
_context{}
|
||||
{
|
||||
}
|
||||
|
||||
// get context by reference, do not take ownership of it
|
||||
template <typename T=Context, typename std::enable_if<!std::is_void<T>::value>::type* = nullptr>
|
||||
explicit AdapterAndContext(Adapter&& adapter, TContext& ctx)
|
||||
: _adapter{std::move(adapter)},
|
||||
_context{ctx}
|
||||
{
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
writeSwapped(buf, count, ShouldSwap<typename Adapter::TConfig>{});
|
||||
}
|
||||
|
||||
AdapterAndContext(const AdapterAndContext &) = delete;
|
||||
AdapterAndContext &operator=(const AdapterAndContext &) = delete;
|
||||
|
||||
// todo conditionally noexcept
|
||||
AdapterAndContext(AdapterAndContext &&) = default;
|
||||
AdapterAndContext &operator=(AdapterAndContext &&) = default;
|
||||
|
||||
TContext& context() {
|
||||
return _context;
|
||||
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.");
|
||||
}
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
void align() {
|
||||
|
||||
}
|
||||
|
||||
OutputAdapterBaseCRTP() = default;
|
||||
OutputAdapterBaseCRTP(const OutputAdapterBaseCRTP&) = delete;
|
||||
OutputAdapterBaseCRTP& operator = (const OutputAdapterBaseCRTP&) = delete;
|
||||
OutputAdapterBaseCRTP(OutputAdapterBaseCRTP&&) = default;
|
||||
OutputAdapterBaseCRTP& operator = (OutputAdapterBaseCRTP&&) = default;
|
||||
|
||||
private:
|
||||
typename std::conditional<std::is_void<Context>::value,
|
||||
TContext,TContext&>::type _context;
|
||||
};
|
||||
|
||||
//this class is used as wrapper for real Adapter, it only stores reference to real thing
|
||||
template<typename AdapterAndCtx>
|
||||
struct AdapterAndContextWrapper {
|
||||
public:
|
||||
using TConfig = typename AdapterAndCtx::TConfig;
|
||||
using TContext = typename AdapterAndCtx::TContext;
|
||||
using TValue = typename AdapterAndCtx::TValue;
|
||||
|
||||
explicit AdapterAndContextWrapper(AdapterAndCtx& adapterAndCtx)
|
||||
: _wrapped{adapterAndCtx}
|
||||
{
|
||||
template<typename T>
|
||||
void writeSwapped(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);
|
||||
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
AdapterAndContextWrapper(const AdapterAndContextWrapper &) = delete;
|
||||
AdapterAndContextWrapper &operator=(const AdapterAndContextWrapper &) = delete;
|
||||
|
||||
AdapterAndContextWrapper(AdapterAndContextWrapper &&) noexcept = default;
|
||||
AdapterAndContextWrapper &operator=(AdapterAndContextWrapper &&) noexcept = default;
|
||||
|
||||
TContext& context() {
|
||||
return _wrapped.context();
|
||||
template<typename T>
|
||||
void writeSwapped(const T *v, size_t count, std::false_type) {
|
||||
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
protected:
|
||||
AdapterAndCtx& _wrapped;
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct InputAdapterBaseCRTP {
|
||||
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T& v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T* buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
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() {
|
||||
|
||||
}
|
||||
|
||||
InputAdapterBaseCRTP() = default;
|
||||
InputAdapterBaseCRTP(const InputAdapterBaseCRTP&) = delete;
|
||||
InputAdapterBaseCRTP& operator = (const InputAdapterBaseCRTP&) = delete;
|
||||
|
||||
InputAdapterBaseCRTP(InputAdapterBaseCRTP&&) = default;
|
||||
InputAdapterBaseCRTP& operator = (InputAdapterBaseCRTP&&) = default;
|
||||
|
||||
virtual ~InputAdapterBaseCRTP() = default;
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
static_cast<Base*>(this)->readInternal(reinterpret_cast<typename Base::TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if necessary
|
||||
_swapDataBits(v, count, ShouldSwap<typename Base::TConfig>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *, size_t , std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
//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_DETAILS_ADAPTER_UTILS_H
|
||||
#define BITSERY_DETAILS_ADAPTER_UTILS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
enum class ReaderError {
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData,
|
||||
InvalidPointer
|
||||
};
|
||||
|
||||
namespace details {
|
||||
/*
|
||||
* size read/write functions
|
||||
*/
|
||||
template <typename Reader>
|
||||
void readSize(Reader& r, size_t& size, size_t maxSize) {
|
||||
uint8_t hb{};
|
||||
r.template readBytes<1>(hb);
|
||||
if (hb < 0x80u) {
|
||||
size = hb;
|
||||
} else {
|
||||
uint8_t lb{};
|
||||
r.template readBytes<1>(lb);
|
||||
if (hb & 0x40u) {
|
||||
uint16_t lw{};
|
||||
r.template readBytes<2>(lw);
|
||||
size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw;
|
||||
} else {
|
||||
size = ((hb & 0x7Fu) << 8) | lb;
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.error(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Writter>
|
||||
void writeSize(Writter& w, const size_t size) {
|
||||
if (size < 0x80u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
if (size < 0x4000u) {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 8) | 0x80u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size));
|
||||
} else {
|
||||
assert(size < 0x40000000u);
|
||||
w.template writeBytes<1>(static_cast<uint8_t>((size >> 24) | 0xC0u));
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(size >> 16));
|
||||
w.template writeBytes<2>(static_cast<uint16_t>(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_ADAPTER_UTILS_H
|
||||
@@ -58,7 +58,7 @@ namespace bitsery {
|
||||
// e.g. instead of writing this:
|
||||
// s.container(c, 100, [](S& s, float& v) { s.ext4b(v, CompactValue{});});
|
||||
// you can write like this
|
||||
// s.container(c, 100, FtorExtValue<2, CompactValue>{});
|
||||
// s.container(c, 100, FtorExtValue2b<CompactValue>{});
|
||||
template<size_t N, typename Ext>
|
||||
struct FtorExtValue : public Ext {
|
||||
template <typename S, typename T>
|
||||
@@ -368,6 +368,96 @@ namespace bitsery {
|
||||
return getFromTupleIfExists<AssertExists, TCast>(ctx, IsExistsConvertibleTupleType<TCast, std::tuple<TArgs...>>{});
|
||||
}
|
||||
|
||||
template <typename Adapter, typename Context>
|
||||
class AdapterAndContextRef {
|
||||
public:
|
||||
static constexpr bool HasContext = true;
|
||||
using Config = typename Adapter::TConfig;
|
||||
|
||||
// constructing adapter in place is important,
|
||||
// because enableBitPacking might create instance with bit write/read enabled adapter wrapper,
|
||||
// which has non trivial destructor
|
||||
template <typename ... TArgs>
|
||||
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
|
||||
: _adapter{std::forward<TArgs>(args)...},
|
||||
_context{ctx}
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
T& context() {
|
||||
return *getContext<true, T>(_context);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull() {
|
||||
return getContext<false, T>(_context);
|
||||
}
|
||||
|
||||
Adapter& adapter() & {
|
||||
return _adapter;
|
||||
}
|
||||
|
||||
Adapter adapter() && {
|
||||
return std::move(_adapter);
|
||||
}
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
Context& _context;
|
||||
};
|
||||
|
||||
template <typename Adapter>
|
||||
class AdapterAndContextRef<Adapter, void> {
|
||||
public:
|
||||
static constexpr bool HasContext = false;
|
||||
using Config = typename Adapter::TConfig;
|
||||
|
||||
template <typename ... TArgs>
|
||||
explicit AdapterAndContextRef(TArgs&& ... args)
|
||||
: _adapter{std::forward<TArgs>(args)...}
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& context() {
|
||||
static_assert(std::is_void<T>::value, "Context is not defined (is void).");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Adapter& adapter() & {
|
||||
return _adapter;
|
||||
}
|
||||
|
||||
Adapter adapter() && {
|
||||
return std::move(_adapter);
|
||||
}
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
};
|
||||
|
||||
/**
|
||||
* other helper meta-functions
|
||||
*/
|
||||
|
||||
template<typename T, template<typename...> class Template>
|
||||
struct IsSpecializationOf : std::false_type {
|
||||
};
|
||||
|
||||
template<template<typename...> class Template, typename... Args>
|
||||
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace bitsery {
|
||||
void deserializeImpl(Des &, Reader &reader, T &v, std::true_type) const {
|
||||
using TUnsigned = SameSizeUnsigned<T>;
|
||||
TUnsigned res{};
|
||||
readBytes(reader, res);
|
||||
readBytes<Reader::TConfig::CheckDataErrors>(reader, res);
|
||||
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace bitsery {
|
||||
w.template writeBytes<1>(static_cast<uint8_t>(val));
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
template<bool CheckErrors, typename Reader, typename T>
|
||||
void readBytes(Reader &r, T &v) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
uint8_t b1{0x80u};
|
||||
@@ -119,10 +119,11 @@ namespace bitsery {
|
||||
r.template readBytes<1>(b1);
|
||||
v += static_cast<T>(b1 & 0x7Fu) << i;
|
||||
}
|
||||
checkReadOverflow<Reader, T>(r, i, b1, std::integral_constant<bool, CheckOverflow>{});
|
||||
handleReadOverflow<Reader, T>(r, i, b1,
|
||||
std::integral_constant<bool, CheckOverflow && CheckErrors>{});
|
||||
}
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
|
||||
void handleReadOverflow(Reader& r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
|
||||
constexpr auto TBITS = sizeof(T)*8;
|
||||
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
|
||||
r.error(bitsery::ReaderError::InvalidData);
|
||||
@@ -130,7 +131,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template <typename Reader, typename T>
|
||||
void checkReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
|
||||
void handleReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BITSERY_EXT_STD_MAP_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include "../details/adapter_utils.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../details/serialization_common.h"
|
||||
//we need this, so we could reserve for non ordered map
|
||||
#include <unordered_map>
|
||||
@@ -55,7 +55,7 @@ namespace bitsery {
|
||||
using TValue = typename T::mapped_type;
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
details::readSize(reader, size, _maxSize, std::integral_constant<bool, Reader::TConfig::CheckDataErrors>{});
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BITSERY_EXT_STD_SET_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../details/adapter_utils.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../details/serialization_common.h"
|
||||
//we need this, so we could reserve for non ordered set
|
||||
#include <unordered_set>
|
||||
@@ -53,7 +53,7 @@ namespace bitsery {
|
||||
using TKey = typename T::key_type;
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
details::readSize(reader, size, _maxSize, std::integral_constant<bool, Reader::TConfig::CheckDataErrors>{});
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
auto hint = obj.begin();
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace bitsery {
|
||||
template<typename Des, typename Reader, typename Fnc, typename ...Ts>
|
||||
void deserialize(Des& des, Reader& reader, std::variant<Ts...>& obj, Fnc&&) const {
|
||||
size_t index{};
|
||||
details::readSize(reader, index, sizeof...(Ts));
|
||||
details::readSize(reader, index, sizeof...(Ts), std::integral_constant<bool, Reader::TConfig::CheckDataErrors>{});
|
||||
this->execIndex(index, obj, [this, &des](auto& data, auto index) {
|
||||
constexpr size_t Index = decltype(index)::value;
|
||||
using TElem = typename std::variant_alternative<Index, std::variant<Ts...>>::type;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include "polymorphism_utils.h"
|
||||
#include "../../details/adapter_utils.h"
|
||||
#include "../../details/adapter_common.h"
|
||||
#include "../../details/serialization_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
@@ -282,7 +282,7 @@ namespace bitsery {
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des& des, Reader& r, T& obj, Fnc&& fnc) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
details::readSize(r, id, 0, std::false_type{});
|
||||
auto& ctx = des.template context<PointerLinkingContext>();
|
||||
auto& alloc = ctx.getAllocator();
|
||||
if (id) {
|
||||
|
||||
@@ -209,7 +209,7 @@ namespace bitsery {
|
||||
void deserialize(Deserializer& des, Reader& reader, TBase* obj,
|
||||
TCreateFnc createFnc, TDestroyFnc destroyFnc) const {
|
||||
size_t derivedIndex{};
|
||||
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
|
||||
details::readSize(reader, derivedIndex, 0, std::false_type{});
|
||||
|
||||
auto baseToDerivedVecIt = _baseToDerivedArray.find(RTTI::template get<TBase>());
|
||||
//base class is known at compile time, so we can assert on this one
|
||||
|
||||
@@ -178,16 +178,26 @@ namespace bitsery {
|
||||
void deserialize(Des &, Reader &reader, T &v, Fnc &&) const {
|
||||
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.error(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
handleInvalidRange(reader, v, std::integral_constant<bool, Reader::TConfig::CheckDataErrors>{});
|
||||
}
|
||||
|
||||
constexpr size_t getRequiredBits() const {
|
||||
return _range.bitsRequired;
|
||||
};
|
||||
private:
|
||||
|
||||
template <typename Reader, typename T>
|
||||
void handleInvalidRange(Reader& reader, T& v, std::true_type) const {
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.error(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Reader, typename T>
|
||||
void handleInvalidRange(Reader&, T&, std::false_type) const {
|
||||
}
|
||||
|
||||
details::RangeSpec<TValue> _range;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,38 +25,149 @@
|
||||
#define BITSERY_SERIALIZER_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "adapter_writer.h"
|
||||
#include "details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename TAdapterWriter>
|
||||
class BasicSerializer {
|
||||
namespace details {
|
||||
template<typename TAdapter>
|
||||
class OutputAdapterBitPackingWrapper {
|
||||
public:
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TAdapter::TConfig;
|
||||
using TValue = typename TAdapter::TValue;
|
||||
|
||||
OutputAdapterBitPackingWrapper(TAdapter& adapter)
|
||||
: _wrapped{adapter}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
~OutputAdapterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
this->_wrapped.template writeBytes<SIZE,T>(v);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
|
||||
assert(v <= (bitsCount < 64
|
||||
? (1ULL << bitsCount) - 1
|
||||
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
align();
|
||||
this->_wrapped.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return this->_wrapped.currentWritePos();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
this->_wrapped.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return this->_wrapped.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
TAdapter& _wrapped;
|
||||
|
||||
using UnsignedType = typename std::make_unsigned<typename TAdapter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedType>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = (std::min)(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value >>= valueSize;
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
//overload for TValue, for better performance
|
||||
void writeBitsInternal(const UnsignedType &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename TOutputAdapter, typename TContext = void>
|
||||
class BasicSerializer: public details::AdapterAndContextRef<TOutputAdapter, TContext> {
|
||||
public:
|
||||
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
|
||||
using BPEnabledType = BasicSerializer<typename std::conditional<TAdapterWriter::BitPackingEnabled,
|
||||
TAdapterWriter,
|
||||
AdapterWriterBitPackingWrapper<TAdapterWriter>>::type>;
|
||||
using BPEnabledType = BasicSerializer<typename std::conditional<TOutputAdapter::BitPackingEnabled,
|
||||
TOutputAdapter,
|
||||
details::OutputAdapterBitPackingWrapper<TOutputAdapter>>::type, TContext>;
|
||||
|
||||
explicit BasicSerializer(TAdapterWriter& writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
T& context() {
|
||||
return *details::getContext<true, T>(_writer.context());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull() {
|
||||
return details::getContext<false, T>(_writer.context());
|
||||
}
|
||||
using details::AdapterAndContextRef<TOutputAdapter, TContext>::AdapterAndContextRef;
|
||||
|
||||
/*
|
||||
* object function
|
||||
@@ -96,7 +207,7 @@ namespace bitsery {
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
|
||||
void value(const T &v) {
|
||||
using TValue = typename details::IntegralFromFundamental<T>::TValue;
|
||||
_writer.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
|
||||
this->_adapter.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -104,7 +215,9 @@ namespace bitsery {
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc),
|
||||
std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{},
|
||||
std::integral_constant<bool, BasicSerializer::HasContext>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -116,7 +229,7 @@ namespace bitsery {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
extension.serialize(*this, _writer, obj, std::forward<Fnc>(fnc));
|
||||
extension.serialize(*this, this->_adapter, obj, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
@@ -126,7 +239,7 @@ namespace bitsery {
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.serialize(*this, _writer, obj, [](BasicSerializer& s, VType &v) { s.value<VSIZE>(v); });
|
||||
extension.serialize(*this, this->_adapter, obj, [](BasicSerializer& s, VType &v) { s.value<VSIZE>(v); });
|
||||
}
|
||||
|
||||
template<typename T, typename Ext>
|
||||
@@ -136,7 +249,7 @@ namespace bitsery {
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.serialize(*this, _writer, obj, [](BasicSerializer& s, VType &v) { s.object(v); });
|
||||
extension.serialize(*this, this->_adapter, obj, [](BasicSerializer& s, VType &v) { s.object(v); });
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,7 +257,7 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
void boolValue(bool v) {
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
procBoolValue(v, std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -183,7 +296,7 @@ namespace bitsery {
|
||||
"use container(const T&, Fnc) overload without `maxSize` for static containers");
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
details::writeSize(this->_adapter, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
@@ -196,7 +309,7 @@ namespace bitsery {
|
||||
static_assert(VSIZE > 0, "");
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
details::writeSize(this->_adapter, size);
|
||||
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
@@ -209,7 +322,7 @@ namespace bitsery {
|
||||
"use container(const T&) overload without `maxSize` for static containers");
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writer, size);
|
||||
details::writeSize(this->_adapter, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
@@ -314,8 +427,6 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
|
||||
TAdapterWriter& _writer;
|
||||
|
||||
//process value types
|
||||
//false_type means that we must process all elements individually
|
||||
template<size_t VSIZE, typename It>
|
||||
@@ -331,7 +442,7 @@ namespace bitsery {
|
||||
using TValue = typename std::decay<decltype(*first)>::type;
|
||||
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
|
||||
if (first != last)
|
||||
_writer.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)),
|
||||
this->_adapter.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)),
|
||||
static_cast<size_t>(std::distance(first, last)));
|
||||
}
|
||||
|
||||
@@ -349,7 +460,7 @@ namespace bitsery {
|
||||
void procText(const T& str, size_t maxSize) {
|
||||
auto length = traits::TextTraits<T>::length(str);
|
||||
assert((length + (traits::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
|
||||
details::writeSize(_writer, length);
|
||||
details::writeSize(this->_adapter, length);
|
||||
auto begin = std::begin(str);
|
||||
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
@@ -363,25 +474,31 @@ namespace bitsery {
|
||||
|
||||
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
|
||||
void procBoolValue(bool v, std::true_type) {
|
||||
_writer.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
|
||||
this->_adapter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
|
||||
}
|
||||
|
||||
void procBoolValue(bool v, std::false_type) {
|
||||
_writer.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
|
||||
this->_adapter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
|
||||
}
|
||||
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
|
||||
template <typename Fnc, typename HasContext>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type, HasContext) {
|
||||
fnc(*this);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::true_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
AdapterWriterBitPackingWrapper<TAdapterWriter> bitPackingWrapper{_writer};
|
||||
BPEnabledType serializer{bitPackingWrapper};
|
||||
fnc(serializer);
|
||||
BPEnabledType ser{this->_context, this->_adapter};
|
||||
fnc(ser);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
BPEnabledType ser{this->_adapter};
|
||||
fnc(ser);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
@@ -403,20 +520,18 @@ namespace bitsery {
|
||||
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
|
||||
template <typename OutputAdapter, typename T>
|
||||
size_t quickSerialization(OutputAdapter adapter, const T& value) {
|
||||
AdapterWriter<OutputAdapter, DefaultConfig> writer{std::move(adapter)};
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>> ser{writer};
|
||||
BasicSerializer<OutputAdapter> ser{std::move(adapter)};
|
||||
ser.object(value);
|
||||
writer.flush();
|
||||
return writer.writtenBytesCount();
|
||||
ser.adapter().flush();
|
||||
return ser.adapter().writtenBytesCount();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t quickMeasureSize(const T& value) {
|
||||
MeasureSize writer{};
|
||||
BasicSerializer<MeasureSize> ser{writer};
|
||||
template <typename Context, typename OutputAdapter, typename T>
|
||||
size_t quickSerialization(Context& ctx, OutputAdapter adapter, const T& value) {
|
||||
BasicSerializer<OutputAdapter, Context> ser{ctx, std::move(adapter)};
|
||||
ser.object(value);
|
||||
writer.flush();
|
||||
return writer.writtenBytesCount();
|
||||
ser.adapter().flush();
|
||||
return ser.adapter().writtenBytesCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,26 +22,31 @@
|
||||
|
||||
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <bitsery/adapter/measure_size.h>
|
||||
#include <bitsery/deserializer.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/adapter/stream.h>
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<char>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::AdapterWriter<OutputAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
using bitsery::ReaderError;
|
||||
|
||||
using testing::Eq;
|
||||
using testing::Ge;
|
||||
|
||||
struct DisableAdapterErrorsConfig {
|
||||
static constexpr bitsery::EndiannessType Endianness = bitsery::DefaultConfig::Endianness;
|
||||
static constexpr bool CheckAdapterErrors = false;
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
};
|
||||
|
||||
TEST(OutputBuffer, WhenInitialBufferIsEmptyThenResizeInAdapterConstructor) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
@@ -53,7 +58,7 @@ TEST(OutputBuffer, WhenInitialBufferIsEmptyThenResizeInAdapterConstructor) {
|
||||
TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
OutputAdapter w{buf};
|
||||
const auto initialSize = buf.size();
|
||||
EXPECT_THAT(buf.size(), Eq(initialSize));
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(0));
|
||||
@@ -65,7 +70,7 @@ TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) {
|
||||
TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCountIsNotAffected) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
OutputAdapter w{buf};
|
||||
const auto initialSize = buf.size();
|
||||
EXPECT_THAT(buf.size(), Eq(initialSize));
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(0));
|
||||
@@ -76,11 +81,22 @@ TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCoun
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8));
|
||||
}
|
||||
|
||||
TEST(OutputBuffer, CanWorkWithFixedSizeBuffer) {
|
||||
//setup data
|
||||
std::array<uint8_t, 10> buf{};
|
||||
bitsery::OutputBufferAdapter<std::array<uint8_t, 10>> w{buf};
|
||||
const auto initialSize = buf.size();
|
||||
EXPECT_THAT(buf.size(), Eq(initialSize));
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(0));
|
||||
w.currentWritePos(5);
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(5));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition) {
|
||||
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
InputAdapter r{buf.begin(), 10};
|
||||
r.currentReadPos(5);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(5));
|
||||
r.currentReadPos(0);
|
||||
@@ -95,7 +111,7 @@ TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) {
|
||||
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
InputAdapter r{buf.begin(), 10};
|
||||
r.currentReadPos(10);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
r.currentReadPos(11);
|
||||
@@ -105,7 +121,7 @@ TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) {
|
||||
TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
InputAdapter r{buf.begin(), 10};
|
||||
r.currentReadEndPos(11);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
}
|
||||
@@ -113,7 +129,7 @@ TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) {
|
||||
TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
InputAdapter r{buf.begin(), 10};
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(0));
|
||||
r.currentReadEndPos(5);
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(5));
|
||||
@@ -124,7 +140,7 @@ TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition)
|
||||
TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 1}};
|
||||
InputAdapter r{buf.begin(), 1};
|
||||
r.currentReadEndPos(1);
|
||||
uint32_t tmp{};
|
||||
r.readBytes<4>(tmp);
|
||||
@@ -140,7 +156,7 @@ TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored
|
||||
TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesntChange) {
|
||||
Buffer buf{};
|
||||
buf.resize(10);
|
||||
Reader r{{buf.begin(), 3}};
|
||||
InputAdapter r{buf.begin(), 3};
|
||||
uint32_t tmp{};
|
||||
r.currentReadEndPos(2);
|
||||
r.readBytes<4>(tmp);
|
||||
@@ -157,7 +173,7 @@ TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesn
|
||||
TEST(InputBuffer, WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAndGettingAlwaysReturnsZero) {
|
||||
Buffer buf{};
|
||||
buf.resize(10);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
InputAdapter r{buf.begin(), 10};
|
||||
uint32_t tmp{};
|
||||
r.readBytes<4>(tmp);
|
||||
r.currentReadEndPos(5);
|
||||
@@ -178,52 +194,79 @@ TEST(InputBuffer, ConstDataForBufferAllAdapters) {
|
||||
//create and write to buffer
|
||||
uint16_t data = 7549;
|
||||
Buffer bufWrite{};
|
||||
Writer bw{bufWrite};
|
||||
OutputAdapter bw{bufWrite};
|
||||
bw.writeBytes<2>(data);
|
||||
bw.flush();
|
||||
const Buffer buf{bufWrite};
|
||||
|
||||
//read from buffer
|
||||
using Adapter1 = bitsery::InputBufferAdapter<const Buffer>;
|
||||
using Adapter2 = bitsery::UnsafeInputBufferAdapter<const Buffer>;
|
||||
|
||||
bitsery::AdapterReader<Adapter1, bitsery::DefaultConfig> r1{Adapter1{buf.begin(), buf.end()}};
|
||||
bitsery::AdapterReader<Adapter2, bitsery::DefaultConfig> r2{Adapter2{buf.begin(), buf.end()}};
|
||||
bitsery::InputBufferAdapter<const Buffer> r1{buf.begin(), buf.end()};
|
||||
|
||||
uint16_t res1{};
|
||||
r1.readBytes<2>(res1);
|
||||
|
||||
uint16_t res2{};
|
||||
r2.readBytes<2>(res2);
|
||||
EXPECT_THAT(res1, Eq(data));
|
||||
EXPECT_THAT(res2, Eq(data));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
TEST(InputBuffer, WhenAdapterErrorsIsDisabledThenCanChangeAnyReadPositionAndReadsAsserts) {
|
||||
//create and write to buffer
|
||||
uint64_t data = 0x1122334455667788;
|
||||
Buffer buf{};
|
||||
OutputAdapter bw{buf};
|
||||
bw.writeBytes<8>(data);
|
||||
bw.flush();
|
||||
|
||||
bitsery::InputBufferAdapter<Buffer, DisableAdapterErrorsConfig> r1{buf.begin(), 2};
|
||||
uint16_t res1{};
|
||||
r1.readBytes<2>(res1);
|
||||
EXPECT_THAT(res1, Eq(0x7788)); // default config is little endian
|
||||
EXPECT_THAT(r1.currentReadPos(), Eq(2));
|
||||
r1.currentReadPos(4);
|
||||
EXPECT_THAT(r1.currentReadPos(), Eq(4));
|
||||
EXPECT_DEATH(r1.readBytes<2>(res1), ""); // default config is little endian
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(InputStream, WhenAdapterErrorsIsDisabledThenReadingPastEndDoesntSetErrorAndDoesntReturnZero) {
|
||||
//create and write to buffer
|
||||
std::stringstream ss{};
|
||||
bitsery::OutputStreamAdapter bw{ss};
|
||||
uint32_t data = 0x12345678;
|
||||
bw.writeBytes<4>(data);
|
||||
bw.flush();
|
||||
|
||||
bitsery::BasicInputStreamAdapter<char, DisableAdapterErrorsConfig, std::char_traits<char>> br{ss};
|
||||
uint32_t res{};
|
||||
br.readBytes<4>(res);
|
||||
EXPECT_THAT(res, Eq(data));
|
||||
br.readBytes<4>(res);
|
||||
EXPECT_THAT(res, Eq(data));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
template <template<typename...> class TAdapter>
|
||||
struct BufferConfig {
|
||||
struct InBufferConfig {
|
||||
using Data = std::vector<char>;
|
||||
using Adapter = TAdapter<Data>;
|
||||
using Reader = bitsery::AdapterReader<Adapter, bitsery::DefaultConfig>;
|
||||
|
||||
Data data{};
|
||||
Reader createReader(const std::vector<char>& buffer) {
|
||||
Adapter createReader(const std::vector<char>& buffer) {
|
||||
data = buffer;
|
||||
return Reader{Adapter{data.begin(), data.size()}};
|
||||
return Adapter{data.begin(), data.size()};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TAdapter>
|
||||
struct StreamConfig {
|
||||
struct InStreamConfig {
|
||||
using Data = std::stringstream;
|
||||
using Adapter = TAdapter;
|
||||
using Reader = bitsery::AdapterReader<Adapter, bitsery::DefaultConfig>;
|
||||
|
||||
Data data{};
|
||||
Reader createReader(const std::vector<char>& buffer) {
|
||||
Adapter createReader(const std::vector<char>& buffer) {
|
||||
std::string str(buffer.begin(), buffer.end());
|
||||
data = std::stringstream{str};
|
||||
return Reader{Adapter{data}};
|
||||
return Adapter{data};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -235,9 +278,8 @@ public:
|
||||
};
|
||||
|
||||
using AdapterInputTypes = ::testing::Types<
|
||||
BufferConfig<bitsery::InputBufferAdapter>,
|
||||
BufferConfig<bitsery::UnsafeInputBufferAdapter>,
|
||||
StreamConfig<bitsery::InputStreamAdapter>
|
||||
InBufferConfig<bitsery::InputBufferAdapter>,
|
||||
InStreamConfig<bitsery::InputStreamAdapter>
|
||||
>;
|
||||
|
||||
template <typename TConfig>
|
||||
@@ -247,18 +289,6 @@ class InputAll: public AdapterConfig<TConfig> {
|
||||
TYPED_TEST_CASE(InputAll, AdapterInputTypes);
|
||||
|
||||
|
||||
using AdapterInputSafeOnlyTypes = ::testing::Types<
|
||||
BufferConfig<bitsery::InputBufferAdapter>,
|
||||
StreamConfig<bitsery::InputStreamAdapter>
|
||||
>;
|
||||
|
||||
template <typename TConfig>
|
||||
class InputSafeOnly: public AdapterConfig<TConfig> {
|
||||
};
|
||||
|
||||
TYPED_TEST_CASE(InputSafeOnly, AdapterInputSafeOnlyTypes);
|
||||
|
||||
|
||||
TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) {
|
||||
auto r = this->config.createReader({0,0,0,0});
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
@@ -270,11 +300,26 @@ TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) {
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
TYPED_TEST(InputAll, CanBeMoveConstructedAndMoveAssigned) {
|
||||
auto r = this->config.createReader({1,2,3});
|
||||
uint8_t res{};
|
||||
r.template readBytes<1>(res);
|
||||
EXPECT_THAT(res, Eq(1));
|
||||
// move construct
|
||||
auto r1 = std::move(r);
|
||||
r1.template readBytes<1>(res);
|
||||
EXPECT_THAT(res, Eq(2));
|
||||
// move assign
|
||||
r = std::move(r1);
|
||||
r.template readBytes<1>(res);
|
||||
EXPECT_THAT(res, Eq(3));
|
||||
}
|
||||
|
||||
|
||||
TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError) {
|
||||
|
||||
auto r = this->config.createReader({0x7F});
|
||||
bitsery::AdapterReaderBitPackingWrapper<decltype(r)> bpr{r};
|
||||
bitsery::details::InputAdapterBitPackingWrapper<decltype(r)> bpr{r};
|
||||
|
||||
uint8_t tmp{0xFF};
|
||||
bpr.readBits(tmp,3);
|
||||
@@ -292,7 +337,7 @@ TYPED_TEST(InputAll, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
OutputAdapter bw{buf};
|
||||
|
||||
bw.writeBytes<4>(tb);
|
||||
bw.writeBytes<2>(tc);
|
||||
@@ -320,62 +365,13 @@ TYPED_TEST(InputAll, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully
|
||||
EXPECT_THAT(rd, Eq(td));
|
||||
}
|
||||
|
||||
TYPED_TEST(InputSafeOnly, WhenAllBytesAreReadWithoutErrorsThenIsCompletedSuccessfully) {
|
||||
//setup data
|
||||
|
||||
uint32_t tb = 94545646;
|
||||
int16_t tc = -8778;
|
||||
uint8_t td = 200;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<4>(tb);
|
||||
bw.writeBytes<2>(tc);
|
||||
bw.writeBytes<1>(td);
|
||||
bw.flush();
|
||||
buf.resize(bw.writtenBytesCount());
|
||||
|
||||
auto br = this->config.createReader(buf);
|
||||
|
||||
uint32_t rb = 94545646;
|
||||
int16_t rc = -8778;
|
||||
uint8_t rd = 200;
|
||||
|
||||
br.template readBytes<4>(rb);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br.template readBytes<2>(rc);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
br.template readBytes<1>(rd);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
br.template readBytes<1>(rd);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br1.template readBytes<4>(rb);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br1.template readBytes<2>(rc);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.template readBytes<2>(rc);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.template readBytes<1>(rd);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
}
|
||||
|
||||
|
||||
TYPED_TEST(InputSafeOnly, WhenReadingMoreThanAvailableThenDataOverflow) {
|
||||
TYPED_TEST(InputAll, WhenReadingMoreThanAvailableThenDataOverflow) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
OutputAdapter w{buf};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
buf.resize(w.writtenBytesCount());
|
||||
@@ -397,12 +393,12 @@ TYPED_TEST(InputSafeOnly, WhenReadingMoreThanAvailableThenDataOverflow) {
|
||||
|
||||
}
|
||||
|
||||
TYPED_TEST(InputSafeOnly, WhenReaderHasErrorsAllThenReadsReturnZero) {
|
||||
TYPED_TEST(InputAll, WhenReaderHasErrorsAllThenReadsReturnZero) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
OutputAdapter w{buf};
|
||||
w.writeBytes<1>(t1);
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
@@ -419,19 +415,83 @@ TYPED_TEST(InputSafeOnly, WhenReaderHasErrorsAllThenReadsReturnZero) {
|
||||
}
|
||||
|
||||
|
||||
template <template<typename...> class TAdapter>
|
||||
struct OutBufferConfig {
|
||||
using Data = std::vector<char>;
|
||||
using Adapter = TAdapter<Data>;
|
||||
|
||||
Data data{};
|
||||
Adapter createWriter() {
|
||||
return Adapter{data};
|
||||
}
|
||||
|
||||
bitsery::InputBufferAdapter<Data> getReader() {
|
||||
return bitsery::InputBufferAdapter<Data>{data.begin(), data.end()};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TAdapter>
|
||||
struct OutStreamConfig {
|
||||
using Data = std::stringstream;
|
||||
using Adapter = TAdapter;
|
||||
|
||||
Data data{};
|
||||
Adapter createWriter() {
|
||||
return Adapter{data};
|
||||
}
|
||||
|
||||
bitsery::InputStreamAdapter getReader() {
|
||||
return bitsery::InputStreamAdapter{data};
|
||||
}
|
||||
};
|
||||
|
||||
using AdapterOutputTypes = ::testing::Types<
|
||||
OutBufferConfig<bitsery::OutputBufferAdapter>,
|
||||
OutStreamConfig<bitsery::OutputStreamAdapter>,
|
||||
OutStreamConfig<bitsery::OutputBufferedStreamAdapter>
|
||||
>;
|
||||
|
||||
template <typename TConfig>
|
||||
class OutputAll: public AdapterConfig<TConfig> {
|
||||
};
|
||||
|
||||
TYPED_TEST_CASE(OutputAll, AdapterOutputTypes);
|
||||
|
||||
TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned) {
|
||||
auto w = this->config.createWriter();
|
||||
uint8_t data{1};
|
||||
w.template writeBytes<1>(data);
|
||||
// move construct
|
||||
auto w1 = std::move(w);
|
||||
data = 2;
|
||||
w1.template writeBytes<1>(data);
|
||||
// move assignment
|
||||
w = std::move(w1);
|
||||
data = 3;
|
||||
w.template writeBytes<1>(data);
|
||||
w.flush();
|
||||
|
||||
auto r = this->config.getReader();
|
||||
r.template readBytes<1>(data);
|
||||
EXPECT_THAT(data, Eq(1));
|
||||
r.template readBytes<1>(data);
|
||||
EXPECT_THAT(data, Eq(2));
|
||||
r.template readBytes<1>(data);
|
||||
EXPECT_THAT(data, Eq(3));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class OutputStreamBuffered : public testing::Test {
|
||||
public:
|
||||
using Buffer = T;
|
||||
using Adapter = bitsery::BasicBufferedOutputStreamAdapter<char, std::char_traits<char>, Buffer>;
|
||||
using Writer = bitsery::AdapterWriter<Adapter, bitsery::DefaultConfig>;
|
||||
using Adapter = bitsery::BasicBufferedOutputStreamAdapter<char, bitsery::DefaultConfig, std::char_traits<char>, Buffer>;
|
||||
|
||||
static constexpr size_t InternalBufferSize = 128;
|
||||
|
||||
std::stringstream stream{};
|
||||
|
||||
Writer writer{{stream, 128}};
|
||||
Adapter writer{stream, 128};
|
||||
};
|
||||
|
||||
using BufferedAdapterInternalBufferTypes = ::testing::Types<
|
||||
@@ -442,7 +502,7 @@ using BufferedAdapterInternalBufferTypes = ::testing::Types<
|
||||
|
||||
TYPED_TEST_CASE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes);
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenBufferOverflowThenWriteBufferAndRemainingDataToStream) {
|
||||
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferAndRemainingDataToStream) {
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
this->writer.template writeBytes<1>(x);
|
||||
@@ -465,7 +525,7 @@ TYPED_TEST(OutputStreamBuffered, WhenBufferIsStackAllocatedThenBufferSizeViaCtor
|
||||
|
||||
//create writer with half the internal buffer size
|
||||
//for std::vector it should overflow, and for std::array it should have no effect
|
||||
typename TestFixture::Writer w{{this->stream, TestFixture::InternalBufferSize / 2}};
|
||||
typename TestFixture::Adapter w{this->stream, TestFixture::InternalBufferSize / 2};
|
||||
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
@@ -497,16 +557,4 @@ TEST(AdapterWriterMeasureSize, CorrectlyMeasuresWrittenBytesCountForSerializatio
|
||||
// doesn't compile on older compilers if I write bitsery::MeasureSize::BitPackingEnabled directly in EXPECT_THAT macro.
|
||||
constexpr bool bpEnabled = bitsery::MeasureSize::BitPackingEnabled;
|
||||
EXPECT_THAT(bpEnabled, Eq(true));
|
||||
}
|
||||
|
||||
|
||||
struct CustomInternalContextConfig: bitsery::DefaultConfig {
|
||||
using InternalContext = std::tuple<int, float>;
|
||||
};
|
||||
|
||||
TEST(AdapterWriterMeasureSize, SupportsInternalAndExternalContexts) {
|
||||
char extCtx{'A'};
|
||||
bitsery::BasicMeasureSize<CustomInternalContextConfig, char> w{extCtx};
|
||||
EXPECT_THAT(w.externalContext(), Eq('A'));
|
||||
std::tuple<int, float>& tmp = w.internalContext();
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,9 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include <bitsery/serializer.h>
|
||||
#include <bitsery/deserializer.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
@@ -38,8 +37,10 @@ constexpr EndiannessType getInverseEndianness(EndiannessType e) {
|
||||
: EndiannessType::LittleEndian;
|
||||
}
|
||||
|
||||
struct InverseEndiannessConfig:public DefaultConfig {
|
||||
static constexpr bitsery::EndiannessType NetworkEndianness = getInverseEndianness(DefaultConfig::NetworkEndianness);
|
||||
struct InverseEndiannessConfig {
|
||||
static constexpr bitsery::EndiannessType Endianness = getInverseEndianness(DefaultConfig::Endianness);
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
static constexpr bool CheckAdapterErrors = true;
|
||||
};
|
||||
|
||||
struct IntegralTypes {
|
||||
@@ -50,7 +51,7 @@ struct IntegralTypes {
|
||||
int8_t e;
|
||||
};
|
||||
|
||||
using InverseReader = bitsery::AdapterReader<InputAdapter, InverseEndiannessConfig>;
|
||||
using InverseReader = bitsery::InputBufferAdapter<Buffer, InverseEndiannessConfig>;
|
||||
|
||||
|
||||
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
@@ -80,7 +81,7 @@ TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
bw.writeBytes<1>(src.e);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
InverseReader br{buf.begin(), bw.writtenBytesCount()};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<4>(res.b);
|
||||
@@ -106,7 +107,7 @@ TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
|
||||
bw.writeBuffer<1>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
InverseReader br{buf.begin(), bw.writtenBytesCount()};
|
||||
br.readBuffer<1>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
@@ -125,7 +126,7 @@ TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
bw.writeBuffer<2>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
InverseReader br{buf.begin(), bw.writtenBytesCount()};
|
||||
br.readBuffer<2>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
@@ -161,15 +162,15 @@ TEST(DataEndianness, WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndiann
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
bitsery::AdapterWriterBitPackingWrapper<Writer> bpw{bw};
|
||||
bitsery::details::OutputAdapterBitPackingWrapper<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
|
||||
InverseReader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<InverseReader> bpr{br};
|
||||
InverseReader br{buf.begin(), bpw.writtenBytesCount()};
|
||||
bitsery::details::InputAdapterBitPackingWrapper<InverseReader> bpr{br};
|
||||
IntegralUnsignedTypes res{};
|
||||
bpr.readBits(res.a, aBITS);
|
||||
bpr.readBits(res.b, bBITS);
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include <bitsery/serializer.h>
|
||||
#include <bitsery/deserializer.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
@@ -29,8 +31,8 @@
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
using AdapterBitPackingWriter = bitsery::AdapterWriterBitPackingWrapper<Writer>;
|
||||
using AdapterBitPackingReader = bitsery::AdapterReaderBitPackingWrapper<Reader>;
|
||||
using AdapterBitPackingWriter = bitsery::details::OutputAdapterBitPackingWrapper<Writer>;
|
||||
using AdapterBitPackingReader = bitsery::details::InputAdapterBitPackingWrapper<Reader>;
|
||||
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
@@ -58,7 +60,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
bpw.writeBits(std::numeric_limits<uint8_t>::max(), 8);
|
||||
bpw.flush();
|
||||
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
Reader br{buf.begin(), bpw.writtenBytesCount()};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint64_t v64{};
|
||||
uint32_t v32{};
|
||||
@@ -106,7 +108,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
|
||||
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
|
||||
EXPECT_THAT(writtenSize, Eq(bytesCount));
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
|
||||
IntegralUnsignedTypes res{};
|
||||
@@ -139,7 +141,7 @@ TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint16_t tmp;
|
||||
bpr.readBits(tmp,4);
|
||||
@@ -150,7 +152,7 @@ TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//part of next byte
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br1{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
@@ -158,7 +160,7 @@ TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//bigger than byte
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br2{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
bpr2.readBits(tmp,9);
|
||||
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
@@ -181,7 +183,7 @@ TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
bpw.flush();
|
||||
|
||||
unsigned char tmp;
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
Reader br{buf.begin(), bpw.writtenBytesCount()};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(tmp, Eq(3u));
|
||||
@@ -214,14 +216,14 @@ TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
unsigned char tmp;
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br1{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
//read aligned bits
|
||||
bpr1.readBits(tmp,6);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br2{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
//read 2 bits
|
||||
bpr2.readBits(tmp,2);
|
||||
@@ -266,7 +268,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
|
||||
EXPECT_THAT(writtenSize, Eq(18));
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br{buf.begin(), writtenSize};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<4>(res.b);
|
||||
br.readBytes<2>(res.c);
|
||||
@@ -312,7 +314,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytesWithBitPackingWrapper) {
|
||||
|
||||
EXPECT_THAT(writtenSize, Eq(18));
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
IntegralTypes res{};
|
||||
bpr.readBytes<4>(res.b);
|
||||
@@ -343,7 +345,7 @@ TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
Reader br1{buf.begin(), bw.writtenBytesCount()};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
@@ -366,7 +368,7 @@ TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
|
||||
EXPECT_THAT(writtenSize, Eq(sizeof(src) + 1));
|
||||
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
Reader br1{buf.begin(), writtenSize};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
uint8_t tmp{};
|
||||
@@ -394,7 +396,7 @@ TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZ
|
||||
bpw.flush();
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
Reader br{buf.begin(), bpw.writtenBytesCount()};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint8_t tmp{};
|
||||
bpr.readBits(tmp, 2);
|
||||
|
||||
@@ -33,7 +33,7 @@ using bitsery::EndiannessType;
|
||||
template <typename BufType>
|
||||
class DataWriting:public testing::Test {
|
||||
public:
|
||||
using TWriter = bitsery::AdapterWriter<bitsery::OutputBufferAdapter<BufType>, bitsery::DefaultConfig>;
|
||||
using TWriter = bitsery::OutputBufferAdapter<BufType>;
|
||||
using TBuffer = BufType;
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bitsery::details::OutputAdapterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
bpw.flush();
|
||||
@@ -88,7 +88,7 @@ TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bitsery::details::OutputAdapterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
@@ -101,7 +101,7 @@ TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
|
||||
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity) {
|
||||
NonFixedContainer buf{};
|
||||
bitsery::AdapterWriter<bitsery::OutputBufferAdapter<NonFixedContainer>, bitsery::DefaultConfig> bw{buf};
|
||||
bitsery::OutputBufferAdapter<NonFixedContainer> bw{buf};
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
uint32_t tmp{};
|
||||
bw.writeBytes<4>(tmp);
|
||||
|
||||
@@ -84,7 +84,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
double td = -454184.48445;
|
||||
bool tb = true;
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(ti);
|
||||
ser.object(te);
|
||||
ser.object(tf);
|
||||
@@ -97,7 +97,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
float rf{};
|
||||
double rd{};
|
||||
bool rb{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(ri);
|
||||
des.object(re);
|
||||
des.object(rf);
|
||||
@@ -119,7 +119,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
double td = -454184.48445;
|
||||
bool tb = true;
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value<sizeof(ti)>(ti);
|
||||
ser.archive(te, tf, td);
|
||||
ser.object(tb);
|
||||
@@ -130,7 +130,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
float rf{};
|
||||
double rd{};
|
||||
bool rb{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.archive(ri, re, rf);
|
||||
des.value8b(rd);
|
||||
des.object(rb);
|
||||
@@ -383,7 +383,7 @@ TEST(FlexibleSyntax, StdSmartPtr) {
|
||||
std::unique_ptr<std::string> dataUnique1{new std::string{"hello world"}};
|
||||
|
||||
bitsery::ext::PointerLinkingContext plctx1{};
|
||||
BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext> ctx;
|
||||
BasicSerializationContext<bitsery::ext::PointerLinkingContext> ctx;
|
||||
ctx.createSerializer(plctx1).archive(dataShared1, dataWeak1, dataUnique1);
|
||||
|
||||
std::shared_ptr<int> resShared1{};
|
||||
|
||||
@@ -182,12 +182,12 @@ TEST(DeserializeNonDefaultConstructible, StdMap) {
|
||||
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
|
||||
data.emplace(NonDefaultConstructible{4}, NonDefaultConstructible{4});
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, bitsery::ext::StdMap{10},[](decltype(ser)& ser, NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
ser.object(key);
|
||||
ser.object(value);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.ext(res, bitsery::ext::StdMap{10},[](decltype(des)& des, NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
des.object(key);
|
||||
des.object(value);
|
||||
@@ -213,7 +213,7 @@ void serialize(S& s, NonPolymorphicPointers& o) {
|
||||
}
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext>;
|
||||
using SerContext = BasicSerializationContext<bitsery::ext::PointerLinkingContext>;
|
||||
SerContext ctx{};
|
||||
NonPolymorphicPointers data{};
|
||||
data.pp = new NonDefaultConstructible{3};
|
||||
@@ -306,7 +306,7 @@ void serialize(S& s, PolymorphicPointers& o) {
|
||||
|
||||
TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
|
||||
using TContext = std::tuple<bitsery::ext::PointerLinkingContext, bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
SerContext ctx{};
|
||||
PolymorphicPointers data{};
|
||||
data.pp = new PolymorphicNDC1{-4};
|
||||
|
||||
48
tests/serialization.cpp
Normal file
48
tests/serialization.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2019 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.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
TEST(Serialization, AdapterCanBeMovedInAndOut) {
|
||||
Buffer buf{};
|
||||
bitsery::BasicSerializer<Writer> ser1{buf};
|
||||
ser1.object(MyStruct1{1, 2});
|
||||
auto writeAdapter = std::move(ser1).adapter();
|
||||
bitsery::BasicSerializer<Writer> ser2(std::move(writeAdapter));
|
||||
ser2.object(MyStruct1{3, 4});
|
||||
auto writtenBytesCount = ser2.adapter().writtenBytesCount();
|
||||
EXPECT_THAT(writtenBytesCount, Eq(MyStruct1::SIZE + MyStruct1::SIZE));
|
||||
|
||||
MyStruct1 res{};
|
||||
bitsery::BasicDeserializer<Reader> des1{buf.begin(), writtenBytesCount};
|
||||
des1.object(res);
|
||||
EXPECT_THAT(res, Eq(MyStruct1{1, 2}));
|
||||
auto readerAdapter = std::move(des1).adapter();
|
||||
bitsery::BasicDeserializer<Reader> des2(std::move(readerAdapter));
|
||||
des2.object(res);
|
||||
EXPECT_THAT(res, Eq(MyStruct1{3, 4}));
|
||||
EXPECT_TRUE(des2.adapter().isCompletedSuccessfully());
|
||||
}
|
||||
@@ -26,10 +26,6 @@
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using Serializer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
|
||||
using Deserializer = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
|
||||
|
||||
TEST(SerializeBooleans, BoolAsBit) {
|
||||
|
||||
@@ -38,13 +34,13 @@ TEST(SerializeBooleans, BoolAsBit) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.enableBitPacking([&t1, &t2](Serializer& sbp) {
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.enableBitPacking([&t1, &t2](SerializationContext::TSerializerBPEnabled& sbp) {
|
||||
sbp.boolValue(t1);
|
||||
sbp.boolValue(t2);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
des.enableBitPacking([&res1, &res2](Deserializer& sbp) {
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.enableBitPacking([&res1, &res2](SerializationContext::TDeserializerBPEnabled& sbp) {
|
||||
sbp.boolValue(res1);
|
||||
sbp.boolValue(res2);
|
||||
});
|
||||
@@ -60,10 +56,10 @@ TEST(SerializeBooleans, BoolAsByte) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.boolValue(t1);
|
||||
ser.boolValue(t2);
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res1);
|
||||
des.boolValue(res2);
|
||||
|
||||
@@ -74,14 +70,14 @@ TEST(SerializeBooleans, BoolAsByte) {
|
||||
|
||||
TEST(SerializeBooleans, WhenReadingBoolByteReadsMoreThanOneThenInvalidDataErrorAndResultIsFalse) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value1b(uint8_t{1});
|
||||
ser.value1b(uint8_t{2});
|
||||
bool res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(true));
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(false));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
@@ -113,11 +113,11 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements
|
||||
SerializationContext ctx{};
|
||||
using TValue = typename TestFixture::TValue;
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(this->src, 1000, [](decltype(ser)& ser, TValue& v) {
|
||||
ser.template value<sizeof(v)>(v);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(this->res, 1000, [](decltype(des)& des, TValue &v) {
|
||||
des.template value<sizeof(v)>(v);
|
||||
//increment by 1 after reading
|
||||
@@ -237,13 +237,13 @@ TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializ
|
||||
using TValue = decltype(*std::begin(res));
|
||||
|
||||
SerializationContext ctx{};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(src, [](decltype(ser)& ser, TValue &v) {
|
||||
char tmp{};
|
||||
ser.object(v);
|
||||
ser.value1b(tmp);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(res, [](decltype(des)& des, TValue &v) {
|
||||
char tmp{};
|
||||
des.object(v);
|
||||
|
||||
@@ -33,8 +33,8 @@ using MultipleTypesContext = std::tuple<int, float, char>;
|
||||
|
||||
TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) {
|
||||
SingleTypeContext ctx{54};
|
||||
BasicSerializationContext<DefaultConfig, SingleTypeContext> c1;
|
||||
auto ser1 = c1.createSerializer(ctx);
|
||||
BasicSerializationContext<SingleTypeContext> c1;
|
||||
auto& ser1 = c1.createSerializer(ctx);
|
||||
|
||||
EXPECT_THAT(ser1.context<SingleTypeContext>(), Eq(ctx));
|
||||
}
|
||||
@@ -42,8 +42,8 @@ TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) {
|
||||
TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) {
|
||||
|
||||
MultipleTypesContext ctx{5, 798.654, 'F'};
|
||||
BasicSerializationContext<DefaultConfig, MultipleTypesContext> c1;
|
||||
auto ser1 = c1.createSerializer(ctx);
|
||||
BasicSerializationContext<MultipleTypesContext> c1;
|
||||
auto& ser1 = c1.createSerializer(ctx);
|
||||
|
||||
EXPECT_THAT(ser1.context<int>(), std::get<0>(ctx));
|
||||
EXPECT_THAT(ser1.context<float>(), std::get<1>(ctx));
|
||||
@@ -52,16 +52,16 @@ TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) {
|
||||
|
||||
TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull) {
|
||||
SingleTypeContext ctx1= 32;
|
||||
BasicSerializationContext<DefaultConfig, SingleTypeContext> c1;
|
||||
auto ser = c1.createSerializer(ctx1);
|
||||
BasicSerializationContext<SingleTypeContext> c1;
|
||||
auto& ser = c1.createSerializer(ctx1);
|
||||
EXPECT_THAT(ser.contextOrNull<char>(), ::testing::IsNull());
|
||||
EXPECT_THAT(ser.contextOrNull<int>(), ::testing::NotNull());
|
||||
*ser.contextOrNull<int>() = 2;
|
||||
EXPECT_THAT(ctx1, Eq(2));
|
||||
|
||||
MultipleTypesContext ctx2{5, 798.654, 'F'};
|
||||
BasicSerializationContext<DefaultConfig, MultipleTypesContext> c2;
|
||||
auto des = c2.createDeserializer(ctx2);
|
||||
BasicSerializationContext<MultipleTypesContext> c2;
|
||||
auto& des = c2.createDeserializer(ctx2);
|
||||
EXPECT_THAT(des.contextOrNull<double>(), ::testing::IsNull());
|
||||
EXPECT_THAT(des.contextOrNull<int>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*des.contextOrNull<char>(), Eq('F'));
|
||||
@@ -73,8 +73,8 @@ struct Derived: Base{};
|
||||
|
||||
TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible) {
|
||||
Derived ctx1{};
|
||||
BasicSerializationContext<DefaultConfig, Derived> c1;
|
||||
auto ser = c1.createSerializer(ctx1);
|
||||
BasicSerializationContext<Derived> c1;
|
||||
auto& ser = c1.createSerializer(ctx1);
|
||||
EXPECT_THAT(ser.contextOrNull<Derived>(), ::testing::NotNull());
|
||||
EXPECT_THAT(ser.contextOrNull<Base>(), ::testing::NotNull());
|
||||
ser.context<Derived>();
|
||||
@@ -87,8 +87,8 @@ TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTak
|
||||
CTX1 ctx1{};
|
||||
std::get<0>(ctx1).value = 1;
|
||||
std::get<2>(ctx1).value = 2;
|
||||
BasicSerializationContext<DefaultConfig, CTX1> c1;
|
||||
auto ser = c1.createSerializer(ctx1);
|
||||
BasicSerializationContext<CTX1> c1;
|
||||
auto& ser = c1.createSerializer(ctx1);
|
||||
EXPECT_THAT(ser.context<Derived>().value, Eq(std::get<2>(ctx1).value));
|
||||
EXPECT_THAT(ser.context<Base>().value, Eq(std::get<0>(ctx1).value));
|
||||
}
|
||||
@@ -98,8 +98,8 @@ TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTak
|
||||
CTX2 ctx2{};
|
||||
std::get<1>(ctx2).value = 1;
|
||||
std::get<2>(ctx2).value = 2;
|
||||
BasicSerializationContext<DefaultConfig, CTX2> c2;
|
||||
auto des = c2.createSerializer(ctx2);
|
||||
BasicSerializationContext<CTX2> c2;
|
||||
auto& des = c2.createSerializer(ctx2);
|
||||
|
||||
EXPECT_THAT(des.context<Derived>().value, Eq(std::get<1>(ctx2).value));
|
||||
//Base will not be accessable in this case, because Derived is first valid match
|
||||
|
||||
@@ -51,21 +51,28 @@ TValue getValue(bool isPositive, size_t significantBits) {
|
||||
}
|
||||
|
||||
// helper function, that serialize and return deserialized value
|
||||
template <typename TSerContext, typename TValue>
|
||||
template <typename TConfig, typename TValue>
|
||||
std::pair<TValue, size_t> serializeAndGetDeserialized(TValue data) {
|
||||
TSerContext ctx;
|
||||
TValue res{};
|
||||
ctx.createSerializer().template ext<sizeof(TValue)>(data, CompactValue{});
|
||||
ctx.createDeserializer().template ext<sizeof(TValue)>(res, CompactValue{});
|
||||
return {res, ctx.getBufferSize()};
|
||||
Buffer buf{};
|
||||
bitsery::BasicSerializer<bitsery::OutputBufferAdapter<Buffer, TConfig>> ser{buf};
|
||||
ser.template ext<sizeof(TValue)>(data, CompactValue{});
|
||||
|
||||
bitsery::BasicDeserializer<bitsery::InputBufferAdapter<Buffer, TConfig>> des{buf.begin(), ser.adapter().writtenBytesCount()};
|
||||
TValue res;
|
||||
des.template ext<sizeof(TValue)>(res, CompactValue{});
|
||||
return {res, ser.adapter().writtenBytesCount()};
|
||||
}
|
||||
|
||||
struct LittleEndianConfig: public bitsery::DefaultConfig {
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
struct LittleEndianConfig {
|
||||
static constexpr EndiannessType Endianness = EndiannessType::LittleEndian;
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
static constexpr bool CheckAdapterErrors = true;
|
||||
};
|
||||
|
||||
struct BigEndianConfig: public bitsery::DefaultConfig {
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::BigEndian;
|
||||
struct BigEndianConfig {
|
||||
static constexpr EndiannessType Endianness = EndiannessType::BigEndian;
|
||||
static constexpr bool CheckDataErrors = true;
|
||||
static constexpr bool CheckAdapterErrors = true;
|
||||
};
|
||||
|
||||
template <typename TValue, bool isPositiveNr, typename TConfig>
|
||||
@@ -120,7 +127,7 @@ TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
|
||||
|
||||
for (auto i = 0u; i < bitsery::details::BitsSize<TValue>::value + 1; ++i) {
|
||||
auto data = getValue<TValue>(tc.isPositive, i);
|
||||
auto res = serializeAndGetDeserialized<BasicSerializationContext<typename TCase::Config, void>>(data);
|
||||
auto res = serializeAndGetDeserialized<typename TCase::Config>(data);
|
||||
EXPECT_THAT(res.first, Eq(data));
|
||||
}
|
||||
}
|
||||
@@ -202,7 +209,7 @@ TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
|
||||
using TValue = typename TCase::Value;
|
||||
TCase tc{};
|
||||
TValue data = getValue<TValue>(tc.isPositive, tc.fillBits);
|
||||
auto res = serializeAndGetDeserialized<SerializationContext>(data);
|
||||
auto res = serializeAndGetDeserialized<bitsery::DefaultConfig>(data);
|
||||
EXPECT_THAT(res.first, Eq(data));
|
||||
EXPECT_THAT(res.second, tc.bytesCount);
|
||||
}
|
||||
@@ -219,9 +226,9 @@ TEST(SerializeExtensionCompactValueEnum, TestEnums) {
|
||||
auto d1 = b1En::E;
|
||||
auto d2 = b8En::B;
|
||||
auto d3 = b8En::F;
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d1).first, Eq(d1));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d2).first, Eq(d2));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d3).first, Eq(d3));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d1).first, Eq(d1));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d2).first, Eq(d2));
|
||||
EXPECT_THAT(serializeAndGetDeserialized<bitsery::DefaultConfig>(d3).first, Eq(d3));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
|
||||
@@ -231,7 +238,7 @@ TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
|
||||
ctx.createSerializer().ext(data, CompactValueAsObject{});
|
||||
ctx.createDeserializer().ext(res, CompactValueAsObject{});
|
||||
EXPECT_THAT(data, ::testing::Ne(res));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ using namespace testing;
|
||||
|
||||
using bitsery::ext::Entropy;
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
using BPDes = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
using BPSer = SerializationContext::TSerializerBPEnabled;
|
||||
using BPDes = SerializationContext::TDeserializerBPEnabled;
|
||||
|
||||
|
||||
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequiredBits) {
|
||||
|
||||
@@ -63,7 +63,7 @@ struct DataV3 {
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDataStarts) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
//session cannot be empty
|
||||
ser.value2b(int16_t{1});
|
||||
ser.ext(int8_t{2}, Growable{}, [] (decltype(ser)& ser, int8_t& v) {
|
||||
@@ -71,7 +71,7 @@ TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDa
|
||||
});
|
||||
ser.value1b(int8_t{3});
|
||||
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
uint8_t res1b{};
|
||||
uint16_t res2b{};
|
||||
uint32_t res4b{};
|
||||
@@ -83,149 +83,149 @@ TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDa
|
||||
EXPECT_THAT(res1b, Eq(2));
|
||||
des.value1b(res1b);
|
||||
EXPECT_THAT(res1b, Eq(3));
|
||||
EXPECT_THAT(ctx.bw->writtenBytesCount(), Eq(8));
|
||||
EXPECT_THAT(ctx.ser->adapter().writtenBytesCount(), Eq(8));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
ser.ext(data, Growable{});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
des.ext(res, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV3 data{8454,987451, 45612};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
ser.ext(data, Growable{});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
des.ext(res, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
ser.ext(data, Growable{});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV3 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
des.ext(res, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
ser.ext(data, Growable{}, [](decltype(ser)& ser, DataV2& o) {
|
||||
ser.value4b(o.v1);
|
||||
ser.value4b(o.v2);
|
||||
bitsery::FtorExtObject<Growable>{}(ser, o);
|
||||
ser.ext(o, Growable{});
|
||||
});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
des.ext(res, Growable{}, [&res, &data](decltype(des)& des, DataV2& o) {
|
||||
des.value4b(o.v1);
|
||||
des.value4b(o.v2);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
bitsery::FtorExtObject<Growable>{}(des, o);
|
||||
des.ext(o, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV3 data{8454,987451, 54124};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
ser.ext(data, Growable{}, [](decltype(ser)& ser, DataV3& o) {
|
||||
ser.value4b(o.v1);
|
||||
ser.value4b(o.v2);
|
||||
bitsery::FtorExtObject<Growable>{}(ser, o);
|
||||
ser.ext(o, Growable{});
|
||||
//new fields can only be added at the end
|
||||
ser.value4b(o.v3);
|
||||
});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
des.ext(res, Growable{}, [&res, &data](decltype(des)& des, DataV2& o) {
|
||||
des.value4b(o.v1);
|
||||
des.value4b(o.v2);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
bitsery::FtorExtObject<Growable>{}(des, o);
|
||||
des.ext(o, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
ser.ext(data, Growable{}, [](decltype(ser)& ser, DataV2& o) {
|
||||
ser.value4b(o.v1);
|
||||
ser.value4b(o.v2);
|
||||
bitsery::FtorExtObject<Growable>{}(ser, o);
|
||||
ser.ext(o, Growable{});
|
||||
});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV3 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
des.ext(res, Growable{}, [&res, &data](decltype(des)& des, DataV3& o) {
|
||||
des.value4b(o.v1);
|
||||
des.value4b(o.v2);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
bitsery::FtorExtObject<Growable>{}(des, o);
|
||||
des.ext(o, Growable{});
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
@@ -234,5 +234,5 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.des->adapter().isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::InheritanceContext>;
|
||||
using SerContext = BasicSerializationContext<bitsery::ext::InheritanceContext>;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ using bitsery::ext::PointerType;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, PointerLinkingContext>;
|
||||
using SerContext = BasicSerializationContext<PointerLinkingContext>;
|
||||
|
||||
class SerializeExtensionPointerSerialization : public testing::Test {
|
||||
public:
|
||||
@@ -61,11 +61,11 @@ public:
|
||||
SerContext sctx1{};
|
||||
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
return sctx1.createSerializer(plctx1);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
return sctx1.createDeserializer(plctx1);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
//linking context in tuple
|
||||
using ContextInTuple = std::tuple<int, PointerLinkingContext, float, char>;
|
||||
ContextInTuple plctx2(0, PointerLinkingContext{}, 0.0f, 'a');
|
||||
BasicSerializationContext<bitsery::DefaultConfig, ContextInTuple> sctx2;
|
||||
BasicSerializationContext<ContextInTuple> sctx2;
|
||||
sctx2.createSerializer(plctx2).ext(data, PointerObserver{});
|
||||
sctx2.createDeserializer(plctx2).ext(data, PointerObserver{});
|
||||
}
|
||||
@@ -121,13 +121,13 @@ TEST(SerializeExtensionPointer, WhenOnlySharedObserverThenPointerLinkingContextI
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(p1null, PointerObserver{});
|
||||
ser.ext(p3null, PointerOwner{});
|
||||
ser.ext(p3null, PointerObserver{});
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(4));
|
||||
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
}
|
||||
@@ -136,7 +136,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
|
||||
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(pd1, PointerOwner{});
|
||||
ser.ext4b(pd2, PointerOwner{});
|
||||
@@ -146,7 +146,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAs
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert1) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext4b(pd2, PointerOwner{});
|
||||
ser1.ext(d3, ReferencedByPointer{});
|
||||
|
||||
@@ -154,14 +154,14 @@ TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPo
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenRererencedByPointerIsSameAsPointerOwnerThenAssert2) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerOwner{});
|
||||
ser1.ext4b(d2, ReferencedByPointer{});
|
||||
EXPECT_DEATH(ser1.ext2b(d1, ReferencedByPointer{}), "");
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAssert) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
EXPECT_DEATH(ser1.ext2b(p1null, PointerOwner{PointerType::NotNull}), "");
|
||||
EXPECT_DEATH(ser1.ext2b(p1null, PointerObserver{PointerType::NotNull}), "");
|
||||
}
|
||||
@@ -169,7 +169,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenNonNullPointerIsNullThenAsser
|
||||
#endif
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerThenIsValid) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerOwner{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
@@ -182,7 +182,7 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerObserverPointsToOwnerT
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedByPointerObservers) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
ser1.ext4b(pd2, PointerObserver{});//points to d2, and d2 is not still marked as owner
|
||||
@@ -194,78 +194,78 @@ TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedB
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext(p3null, PointerOwner{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2));
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(2));
|
||||
size_t res;
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(0));
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(0));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(4));
|
||||
size_t res;
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(1));
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(2));
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(2));
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), res, 0, std::false_type{});
|
||||
EXPECT_THAT(res, Eq(0));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3));
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(3));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAndObject) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext2b(d1, ReferencedByPointer{});
|
||||
ser1.ext4b(d2, ReferencedByPointer{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
auto des = createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3 + 6));
|
||||
auto& des = createDeserializer();
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(3 + 6));
|
||||
size_t id{};
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
|
||||
EXPECT_THAT(id, Eq(1));
|
||||
des.value2b(r1);
|
||||
EXPECT_THAT(r1, Eq(d1));
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
|
||||
EXPECT_THAT(id, Eq(2));
|
||||
des.value4b(r2);
|
||||
EXPECT_THAT(r2, Eq(d2));
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
|
||||
EXPECT_THAT(id, Eq(2));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
|
||||
auto ser1 = createSerializer();
|
||||
auto& ser1 = createSerializer();
|
||||
ser1.ext4b(pd2, PointerOwner{});
|
||||
ser1.ext(pd3, PointerOwner{});
|
||||
auto des1 = createDeserializer();
|
||||
auto& des1 = createDeserializer();
|
||||
//2x ids + int32_t + MyStruct1
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
|
||||
EXPECT_THAT(sctx1.ser->adapter().writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
|
||||
size_t id;
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
|
||||
des1.value4b(r2);
|
||||
EXPECT_THAT(r2, Eq(*pd2));
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
bitsery::details::readSize(sctx1.des->adapter(), id, 0, std::false_type{});
|
||||
des1.object(r3);
|
||||
EXPECT_THAT(r3, Eq(*pd3));
|
||||
}
|
||||
@@ -276,11 +276,11 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext2b(d1, ReferencedByPointer{});
|
||||
ser.ext4b(d2, ReferencedByPointer{});
|
||||
ser.ext(d3, ReferencedByPointer{});
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext2b(r1, ReferencedByPointer{});
|
||||
des.ext4b(r2, ReferencedByPointer{});
|
||||
des.ext(r3, ReferencedByPointer{});
|
||||
@@ -291,32 +291,32 @@ TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNullPointerThenInvalidPointerError) {
|
||||
auto ser = createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
auto& ser = createSerializer();
|
||||
bitsery::details::writeSize(sctx1.ser->adapter(), 0u);
|
||||
ser.ext2b(d1, ReferencedByPointer{});
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext2b(r1, ReferencedByPointer{});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenNonNullPointerIsNullThenInvalidPointerError) {
|
||||
createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
auto des1 = createDeserializer();
|
||||
bitsery::details::writeSize(sctx1.ser->adapter(), 0u);
|
||||
auto& des1 = createDeserializer();
|
||||
des1.ext2b(p1null, PointerOwner{PointerType::NotNull});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
|
||||
auto des2 = createDeserializer();
|
||||
auto& des2 = createDeserializer();
|
||||
des2.ext2b(p1null, PointerObserver{PointerType::NotNull});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_THAT(sctx1.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext2b(pd1, PointerOwner{});
|
||||
ser.ext4b(pd2, PointerOwner{});
|
||||
ser.ext(pd3, PointerOwner{});
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext2b(p1null, PointerOwner{});
|
||||
des.ext4b(p2null, PointerOwner{});
|
||||
des.ext(p3null, PointerOwner{});
|
||||
@@ -331,11 +331,11 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerCreatesObjects) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext4b(p2null, PointerOwner{});
|
||||
ser.ext(p3null, PointerOwner{});
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
//pr cannot link to local variables, need to allocate them separately
|
||||
pr1 = new int16_t{};
|
||||
pr2 = new MyEnumClass{};
|
||||
@@ -351,7 +351,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerOwnerDestroysObjects) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
//first owner, than observer
|
||||
ser.ext4b(d2, ReferencedByPointer{});
|
||||
ser.ext2b(p1null, PointerObserver{});
|
||||
@@ -359,7 +359,7 @@ TEST_F(SerializeExtensionPointerDeserialization, PointerObserver) {
|
||||
//first observer, than owner
|
||||
ser.ext(pd3, PointerObserver{});
|
||||
ser.ext(pd3, PointerOwner{});
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext4b(r2, ReferencedByPointer{});
|
||||
des.ext2b(pr1, PointerObserver{});
|
||||
des.ext4b(p2null, PointerObserver{});
|
||||
@@ -463,12 +463,12 @@ TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOv
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto ser = sctx1.createSerializer(plctx1);
|
||||
auto& ser = sctx1.createSerializer(plctx1);
|
||||
ser.ext(data, PointerOwner{}, [](decltype(ser)& ser, MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto des = sctx1.createDeserializer(plctx1);
|
||||
auto& des = sctx1.createDeserializer(plctx1);
|
||||
des.ext(res, PointerOwner{}, [](decltype(des)& des,MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
@@ -489,12 +489,12 @@ TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto ser = sctx1.createSerializer(plctx1);
|
||||
auto& ser = sctx1.createSerializer(plctx1);
|
||||
ser.ext(data, ReferencedByPointer{}, [](decltype(ser)& ser,MyStruct1 &o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
auto des = sctx1.createDeserializer(plctx1);
|
||||
auto& des = sctx1.createDeserializer(plctx1);
|
||||
des.ext(res, ReferencedByPointer{}, [](decltype(des)& des,MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
|
||||
@@ -42,7 +42,7 @@ using bitsery::ext::ReferencedByPointer;
|
||||
using testing::Eq;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
@@ -148,16 +148,16 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
auto res = sctx.createSerializer(plctx);
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
auto& res = sctx.createSerializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
auto res = sctx.createDeserializer(plctx);
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
auto& res = sctx.createDeserializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
@@ -319,10 +319,10 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
|
||||
BaseClone *baseRes = nullptr; //this class will be registered, but it doesn't have relationships specified via PolymorphicBaseClass
|
||||
auto des = sctx.createDeserializer(plctx);
|
||||
auto& des = sctx.createDeserializer(plctx);
|
||||
auto &pc = std::get<2>(plctx);
|
||||
pc.clear();
|
||||
pc.registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<BaseClone>{});
|
||||
des.ext(baseRes, PointerOwner{});
|
||||
EXPECT_THAT(sctx.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_THAT(sctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ using bitsery::ext::StdSmartPtr;
|
||||
using testing::Eq;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
@@ -172,8 +172,8 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
auto res = sctx.createSerializer(plctx);
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
auto& res = sctx.createSerializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(
|
||||
@@ -181,8 +181,8 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
auto res = sctx.createDeserializer(plctx);
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
auto& res = sctx.createDeserializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(
|
||||
|
||||
@@ -66,11 +66,11 @@ TEST(SerializeExtensionStdSet, FunctionSyntax) {
|
||||
SerializationContext ctx1;
|
||||
std::unordered_multiset<int32_t> t1{54,-484,841,79};
|
||||
std::unordered_multiset<int32_t> r1{74,878,15,16,-7,5,-4,8,7};
|
||||
auto ser = ctx1.createSerializer();
|
||||
auto& ser = ctx1.createSerializer();
|
||||
ser.ext(t1, StdSet{10}, [](decltype(ser)& ser, int32_t& v) {
|
||||
ser.value4b(v);
|
||||
});
|
||||
auto des = ctx1.createDeserializer();
|
||||
auto& des = ctx1.createDeserializer();
|
||||
des.ext(r1, StdSet{10}, [](decltype(des)& des, int32_t& v) {
|
||||
des.value4b(v);
|
||||
});
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
using TExt = typename T::TExt;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
@@ -120,11 +120,11 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
return sctx.createSerializer(plctx);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
return sctx.createDeserializer(plctx);
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
using TExt = typename T::TExt;
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
@@ -154,8 +154,8 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
auto res = sctx.createSerializer(plctx);
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
auto& res = sctx.createSerializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(
|
||||
@@ -163,8 +163,8 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
auto res = sctx.createDeserializer(plctx);
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
auto& res = sctx.createDeserializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(
|
||||
@@ -266,13 +266,13 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new MyStruct1{3, 78}};
|
||||
auto ser = this->createSerializer();
|
||||
auto& ser = this->createSerializer();
|
||||
ser.ext(data, Ext{}, [](decltype(ser)& ser, MyStruct1& o) {
|
||||
//serialize only one field
|
||||
ser.value4b(o.i1);
|
||||
});
|
||||
Ptr res{new MyStruct1{97, 12}};
|
||||
auto des = this->createDeserializer();
|
||||
auto& des = this->createDeserializer();
|
||||
des.ext(res, Ext{}, [](decltype(des)& des, MyStruct1& o) {
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
@@ -298,12 +298,12 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPtrThenPointerO
|
||||
|
||||
Ptr data{new uint16_t{3}};
|
||||
uint16_t* dataObs = data.get();
|
||||
auto ser = this->createSerializer();
|
||||
auto& ser = this->createSerializer();
|
||||
ser.ext2b(data, Ext{});
|
||||
ser.ext2b(dataObs, PointerObserver{});
|
||||
Ptr res{};
|
||||
uint16_t* resObs = nullptr;
|
||||
auto des = this->createDeserializer();
|
||||
auto& des = this->createDeserializer();
|
||||
des.ext2b(res, Ext{});
|
||||
des.ext2b(resObs, PointerObserver{});
|
||||
|
||||
@@ -316,12 +316,12 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, FirstPointerObserver
|
||||
|
||||
Ptr data{new uint16_t{3}};
|
||||
uint16_t* dataObs = data.get();
|
||||
auto ser = this->createSerializer();
|
||||
auto& ser = this->createSerializer();
|
||||
ser.ext2b(dataObs, PointerObserver{});
|
||||
ser.ext2b(data, Ext{});
|
||||
Ptr res{};
|
||||
uint16_t* resObs = nullptr;
|
||||
auto des = this->createDeserializer();
|
||||
auto& des = this->createDeserializer();
|
||||
des.ext2b(resObs, PointerObserver{});
|
||||
des.ext2b(res, Ext{});
|
||||
EXPECT_THAT(resObs, Eq(res.get()));
|
||||
@@ -394,7 +394,7 @@ class SerializeExtensionStdSmartSharedPtr : public testing::Test {
|
||||
public:
|
||||
|
||||
using TContext = std::tuple<PointerLinkingContext, InheritanceContext, PolymorphicContext<StandardRTTI>>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, TContext>;
|
||||
using SerContext = BasicSerializationContext<TContext>;
|
||||
|
||||
//this is useful for PolymorphicContext to bind classes to serializer/deserializer
|
||||
using TSerializer = typename SerContext::TSerializer;
|
||||
@@ -403,16 +403,16 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
auto res = sctx.createSerializer(plctx);
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
auto& res = sctx.createSerializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind serializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
auto res = sctx.createDeserializer(plctx);
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
auto& res = sctx.createDeserializer(plctx);
|
||||
std::get<2>(plctx).clear();
|
||||
//bind deserializer with classes
|
||||
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
|
||||
@@ -436,7 +436,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::shared_ptr<Base> baseData2{baseData1};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
createDeserializer();
|
||||
@@ -453,10 +453,10 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, PointerLinkingContextCorrectlyClearS
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(2));
|
||||
clearSharedState();
|
||||
@@ -469,7 +469,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::shared_ptr<Base> baseData2{new Derived{55, 11}};
|
||||
std::shared_ptr<Base> baseData21{baseData2};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData21, StdSmartPtr{});
|
||||
@@ -477,7 +477,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, CorrectlyManagesSameSharedObject) {
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<Base> baseRes2{};
|
||||
std::shared_ptr<Base> baseRes21{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes21, StdSmartPtr{});
|
||||
@@ -500,7 +500,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseData11{baseData1};
|
||||
std::weak_ptr<Base> baseData12{baseData11};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData11, StdSmartPtr{});
|
||||
ser.ext(baseData12, StdSmartPtr{});
|
||||
@@ -508,7 +508,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstSharedThenWeakPtr) {
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
std::weak_ptr<Base> baseRes12{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes11, StdSmartPtr{});
|
||||
des.ext(baseRes12, StdSmartPtr{});
|
||||
@@ -531,7 +531,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
|
||||
std::shared_ptr<MyStruct1> baseData1{new MyStruct1{3, 78}};
|
||||
std::weak_ptr<MyStruct1> baseData11{baseData1};
|
||||
std::weak_ptr<MyStruct1> baseData2{};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData11, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
@@ -539,7 +539,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FirstWeakThenSharedPtr) {
|
||||
std::shared_ptr<MyStruct1> baseRes1{};
|
||||
std::weak_ptr<MyStruct1> baseRes11{};
|
||||
std::weak_ptr<MyStruct1> baseRes2{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes11, StdSmartPtr{});
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
@@ -560,13 +560,13 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstPolymorphicData0Result1)
|
||||
|
||||
std::shared_ptr<Base> baseData1{};
|
||||
std::weak_ptr<Base> baseData2{};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Base{}};
|
||||
std::weak_ptr<Base> baseRes2{baseRes1};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
@@ -583,13 +583,13 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WeakPtrFirstNonPolymorphicData0Resul
|
||||
|
||||
std::shared_ptr<MyStruct2> baseData1{};
|
||||
std::weak_ptr<MyStruct2> baseData2{};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<MyStruct2> baseRes1{new MyStruct2{MyStruct2::MyEnum::V4, {1, 87}}};
|
||||
std::weak_ptr<MyStruct2> baseRes2{baseRes1};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
@@ -608,7 +608,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
|
||||
std::shared_ptr<Base> baseData2{};
|
||||
std::weak_ptr<Base> baseData3{};
|
||||
std::weak_ptr<Base> baseData11{baseData1};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
ser.ext(baseData3, StdSmartPtr{});
|
||||
@@ -618,7 +618,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
|
||||
std::shared_ptr<Base> baseRes2{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseRes3{baseRes2};
|
||||
std::weak_ptr<Base> baseRes11{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
des.ext(baseRes3, StdSmartPtr{});
|
||||
@@ -639,11 +639,11 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, FewPtrsAreEmpty) {
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Derived{0, 0}};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
clearSharedState();
|
||||
@@ -656,11 +656,11 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsSameType) {
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType) {
|
||||
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{new Base{}};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
clearSharedState();
|
||||
@@ -674,7 +674,7 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenResultObjectExistsDifferentType)
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPointerCointextIsInvalid) {
|
||||
std::shared_ptr<Base> tmp{new Derived{3, 78}};
|
||||
std::weak_ptr<Base> baseData1{tmp};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
EXPECT_FALSE(isPointerContextValid());
|
||||
@@ -682,11 +682,11 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsSerializedThenPoint
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr, WhenOnlyWeakPtrIsDeserializedThenPointerCointextIsInvalid) {
|
||||
std::shared_ptr<Base> baseData1{new Derived{3, 78}};
|
||||
auto ser = createSerializer();
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
|
||||
std::weak_ptr<Base> baseRes1{};
|
||||
auto des = createDeserializer();
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
|
||||
EXPECT_FALSE(isPointerContextValid());
|
||||
|
||||
@@ -50,8 +50,8 @@ TEST(SerializeExtensionValueRange, RequiredBitsIsConstexpr) {
|
||||
|
||||
#endif
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
using BPDes = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
using BPSer = SerializationContext::TSerializerBPEnabled;
|
||||
using BPDes = SerializationContext::TDeserializerBPEnabled;
|
||||
|
||||
|
||||
TEST(SerializeExtensionValueRange, IntegerNegative) {
|
||||
@@ -206,5 +206,5 @@ TEST(SerializeExtensionValueRange, WhenDataIsInvalidThenReturnMinimumRangeValue)
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, Eq(4));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
z.x = X{ 234 };
|
||||
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(y);
|
||||
ser.object(z);
|
||||
|
||||
@@ -107,7 +107,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
Y yres{};
|
||||
Z zres{};
|
||||
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(yres);
|
||||
des.object(zres);
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ using testing::Eq;
|
||||
|
||||
bool SerializeDeserializeContainerSize(SerializationContext& ctx, const size_t size) {
|
||||
std::vector<char> t1(size);
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(t1, size+1, [](decltype(ser)& , char& ){});
|
||||
t1.clear();
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(t1, size+1, [](decltype(des)&, char& ){});
|
||||
return t1.size() == size;
|
||||
}
|
||||
|
||||
@@ -87,71 +87,66 @@ void serialize(S&s, MyStruct2& o) {
|
||||
}
|
||||
|
||||
using Buffer = std::vector<char>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::AdapterWriter<OutputAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::InputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::OutputBufferAdapter<Buffer>;
|
||||
|
||||
|
||||
template <typename Config, typename Context>
|
||||
template <typename Context>
|
||||
class BasicSerializationContext {
|
||||
public:
|
||||
using TWriter = bitsery::AdapterWriter<OutputAdapter, Config, Context>;
|
||||
using TReader = bitsery::AdapterReader<InputAdapter, Config, Context>;
|
||||
using TSerializer = bitsery::BasicSerializer<TWriter>;
|
||||
using TDeserializer = bitsery::BasicDeserializer<TReader>;
|
||||
using TSerializer = bitsery::BasicSerializer<Writer, Context>;
|
||||
using TDeserializer = bitsery::BasicDeserializer<Reader, Context>;
|
||||
using TSerializerBPEnabled = typename TSerializer::BPEnabledType;
|
||||
using TDeserializerBPEnabled = typename TDeserializer::BPEnabledType;
|
||||
|
||||
Buffer buf{};
|
||||
std::unique_ptr<TWriter> bw{};
|
||||
std::unique_ptr<TReader> br{};
|
||||
std::unique_ptr<TSerializer> ser{};
|
||||
std::unique_ptr<TDeserializer> des{};
|
||||
|
||||
template <typename T=Context, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
|
||||
TSerializer createSerializer() {
|
||||
if (!bw) {
|
||||
bw = std::unique_ptr<TWriter>(new TWriter{OutputAdapter{buf}});
|
||||
TSerializer& createSerializer() {
|
||||
if (!ser) {
|
||||
ser = std::unique_ptr<TSerializer>(new TSerializer{buf});
|
||||
}
|
||||
return TSerializer{*bw};
|
||||
return *ser;
|
||||
}
|
||||
|
||||
template <typename T=Context>
|
||||
TSerializer createSerializer(typename std::enable_if<!std::is_void<T>::value, T>::type& ctx) {
|
||||
if (!bw) {
|
||||
bw = std::unique_ptr<TWriter>(new TWriter{OutputAdapter{buf}, ctx});
|
||||
TSerializer& createSerializer(typename std::enable_if<!std::is_void<T>::value, T>::type& ctx) {
|
||||
if (!ser) {
|
||||
ser = std::unique_ptr<TSerializer>(new TSerializer{ctx, buf});
|
||||
}
|
||||
return TSerializer{*bw};
|
||||
return *ser;
|
||||
}
|
||||
|
||||
|
||||
template <typename T=Context, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
|
||||
TDeserializer createDeserializer() {
|
||||
TDeserializer& createDeserializer() {
|
||||
size_t writtenBytes = 0;
|
||||
if (bw) {
|
||||
bw->flush();
|
||||
writtenBytes = bw->writtenBytesCount();
|
||||
if (ser) {
|
||||
ser->adapter().flush();
|
||||
writtenBytes = ser->adapter().writtenBytesCount();
|
||||
}
|
||||
if (!br) {
|
||||
br = std::unique_ptr<TReader>(new TReader{InputAdapter{buf.begin(), writtenBytes}});
|
||||
if (!des) {
|
||||
des = std::unique_ptr<TDeserializer>(new TDeserializer{buf.begin(), writtenBytes});
|
||||
}
|
||||
return TDeserializer{*br};
|
||||
return *des;
|
||||
}
|
||||
|
||||
template <typename T=Context>
|
||||
TDeserializer createDeserializer(typename std::enable_if<!std::is_void<T>::value, T>::type& ctx) {
|
||||
TDeserializer& createDeserializer(typename std::enable_if<!std::is_void<T>::value, T>::type& ctx) {
|
||||
size_t writtenBytes = 0;
|
||||
if (bw) {
|
||||
bw->flush();
|
||||
writtenBytes = bw->writtenBytesCount();
|
||||
if (ser) {
|
||||
ser->adapter().flush();
|
||||
writtenBytes = ser->adapter().writtenBytesCount();
|
||||
}
|
||||
if (!br) {
|
||||
br = std::unique_ptr<TReader>(new TReader{InputAdapter{buf.begin(), writtenBytes}, ctx});
|
||||
if (!des) {
|
||||
des = std::unique_ptr<TDeserializer>(new TDeserializer{ctx, buf.begin(), writtenBytes});
|
||||
}
|
||||
return TDeserializer{*br};
|
||||
return *des;
|
||||
}
|
||||
|
||||
size_t getBufferSize() const {
|
||||
return bw->writtenBytesCount();
|
||||
return ser->adapter().writtenBytesCount();
|
||||
}
|
||||
|
||||
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
|
||||
@@ -167,6 +162,6 @@ public:
|
||||
};
|
||||
|
||||
//helper type
|
||||
using SerializationContext = BasicSerializationContext<bitsery::DefaultConfig, void>;
|
||||
using SerializationContext = BasicSerializationContext<void>;
|
||||
|
||||
#endif //BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
|
||||
@@ -125,5 +125,5 @@ TEST(SerializeText, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidDataError) {
|
||||
std::string tmp = "larger text then allowed";
|
||||
ctx.createSerializer().text1b(tmp,100);
|
||||
ctx.createDeserializer().text1b(tmp, 10);
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
EXPECT_THAT(ctx.des->adapter().error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
Reference in New Issue
Block a user