mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 00:03:54 +00:00
multiple breaking change improvements
This commit is contained in:
committed by
Mindaugas Vinkelis
parent
57dd028b7a
commit
1822796f2e
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,5 +1,46 @@
|
||||
### Improvements
|
||||
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).
|
||||
# current draft
|
||||
|
||||
## removed stuff
|
||||
* TContext\* context() from serializer/deserializer
|
||||
* 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
|
||||
|
||||
## 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
|
||||
* 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
|
||||
* 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)
|
||||
|
||||
## improvements
|
||||
|
||||
* 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.
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
# [4.6.1](https://github.com/fraillt/bitsery/compare/v4.6.0...v4.6.1) (2019-06-27)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace MyTypes {
|
||||
//compress path in a range of -1.0 .. 1.0 with 0.01 precision
|
||||
//enableBitPacking creates separate serializer/deserializer object, that contains bit packing operations
|
||||
s.enableBitPacking([&o](typename S::BPEnabledType& sbp) {
|
||||
sbp.container(o.path, 1000, [&sbp](Vec3& vec3) {
|
||||
sbp.container(o.path, 1000, [](typename S::BPEnabledType& sbp, Vec3& vec3) {
|
||||
constexpr bitsery::ext::ValueRange<float> range{-1.0f,1.0f, 0.01f};
|
||||
sbp.ext(vec3.x, range);
|
||||
sbp.ext(vec3.y, range);
|
||||
|
||||
@@ -58,7 +58,7 @@ void serialize(S& s, MyVariant& o) {
|
||||
// we can also override default 'serialize' function by creating an overloading for that type
|
||||
[](S& s, MyStruct& o) {
|
||||
s.value4b(o.f);
|
||||
s.container(o.v, 1000, [&s](int32_t& v) {
|
||||
s.container(o.v, 1000, [](S& s, int32_t& v) {
|
||||
s.ext4b(v, ext::CompactValue{});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -34,15 +34,15 @@ namespace MyTypes {
|
||||
template<typename S>
|
||||
void serialize(S& s, GameState &o) {
|
||||
//we can have multiple types in context with std::tuple
|
||||
//this cast also works if our context is the same as cast
|
||||
auto maxMonsters = s.template context<int>();
|
||||
//all data from context is always pointer
|
||||
//if data type doesn't match then it will be compile time error
|
||||
auto dmgRange = s.template context<std::pair<uint32_t, uint32_t>>();
|
||||
s.container(o.monsters, *maxMonsters, [&s, dmgRange] (Monster& m) {
|
||||
//NOTE: if context is optional then you can call contextOrNull<T>, and it will return null if T doesn't exists
|
||||
auto maxMonsters = s.template context<int>();
|
||||
auto& dmgRange = s.template context<std::pair<uint32_t, uint32_t>>();
|
||||
|
||||
s.container(o.monsters, maxMonsters, [&dmgRange] (S& s, Monster& m) {
|
||||
s.text1b(m.name, 20);
|
||||
//we know min/max damage range for monsters, so we can use this range instead of full value
|
||||
bitsery::ext::ValueRange<uint32_t> range{dmgRange->first, dmgRange->second};
|
||||
bitsery::ext::ValueRange<uint32_t> range{dmgRange.first, dmgRange.second};
|
||||
//enable bit packing
|
||||
s.enableBitPacking([&m, &range](typename S::BPEnabledType& sbp) {
|
||||
sbp.ext(m.minDamage, range);
|
||||
@@ -53,26 +53,24 @@ namespace MyTypes {
|
||||
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
//context can contain multiple types
|
||||
//it would make more sense to define separate structure for context, but for sake of this example make it more complex
|
||||
//in serialization function we can cast it like this:
|
||||
//context can contain multiple types by wrapping these types in std::tuple
|
||||
//in serialization function we can get type that we need like this:
|
||||
// s.template context<int>();
|
||||
//if we want to get whole tuple, just call s.context() without template paramter.
|
||||
//this templated version also works if our context is the same as cast:
|
||||
// struct MyContext {...};
|
||||
// ...
|
||||
// s.template context<MyContext>();
|
||||
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
|
||||
|
||||
//NOTE:
|
||||
// if your context has no additional usage outside of serialization flow,
|
||||
// then you can create it internally via configuration (see inheritance.cpp)
|
||||
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>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -92,18 +90,18 @@ int main() {
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//pass game mode object to serializer as context
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, bitsery::DefaultConfig>, Context> ser{buffer, &ctx};
|
||||
//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);
|
||||
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
writer.flush();
|
||||
|
||||
MyTypes::GameState res{};
|
||||
BasicDeserializer <AdapterReader<InputAdapter, bitsery::DefaultConfig>, Context> des { InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
Reader reader {{buffer.begin(), writer.writtenBytesCount()}, ctx};
|
||||
BasicDeserializer<Reader> des {reader };
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ void serialize(S& s, MyStruct& o) {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Stream = std::fstream;
|
||||
//buffered stream adapter allows for faster writes
|
||||
using Writer = AdapterWriter<OutputBufferedStreamAdapter, DefaultConfig>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -31,17 +31,18 @@ int main() {
|
||||
|
||||
//open file stream for writing and reading
|
||||
auto fileName = "test_file.bin";
|
||||
Stream s{fileName, s.binary | s.trunc | s.out};
|
||||
std::fstream s{fileName, s.binary | s.trunc | s.out};
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for writing\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
|
||||
//for serialization we can use buffered stream adapter, it can greatly improve performance for some streams
|
||||
Serializer<OutputBufferedStreamAdapter> ser{s};
|
||||
Writer writer{s};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.object(data);
|
||||
//flush to writer
|
||||
AdapterAccess::getWriter(ser).flush();
|
||||
writer.flush();
|
||||
s.close();
|
||||
//reopen for reading
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace MyTypes {
|
||||
template <typename S>
|
||||
void serialize (S& s) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(*this, bitsery::ext::Growable{}, [&s](Weapon& o1) {
|
||||
s.ext(*this, bitsery::ext::Growable{}, [](S& s, Weapon& o1) {
|
||||
s.text1b(o1.name, 20);
|
||||
s.value2b(o1.damage);
|
||||
});
|
||||
@@ -54,7 +54,7 @@ namespace MyTypes {
|
||||
template <typename S>
|
||||
void serialize (S& s, Monster& o) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(o, bitsery::ext::Growable{}, [&s](Monster& o1) {
|
||||
s.ext(o, bitsery::ext::Growable{}, [](S& s, Monster& o1) {
|
||||
s.value1b(o1.color);
|
||||
s.value2b(o1.mana);
|
||||
s.value2b(o1.hp);
|
||||
@@ -75,11 +75,6 @@ using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
//create configuration that enables session support, to work with "growable" extension
|
||||
struct SessionsEnabled:public DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
@@ -89,17 +84,11 @@ int main() {
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//since we're using different configuration, we cannot use quickSerialization function.
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, SessionsEnabled>> ser{OutputAdapter{buffer}};
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
MyTypes::Monster res{};
|
||||
//deserialize
|
||||
BasicDeserializer<AdapterReader<InputAdapter, SessionsEnabled>> des{InputAdapter{buffer.begin(), writtenSize}};
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// BaseClass - normal inheritance
|
||||
// VirtualBaseClass - when virtual inheritance is used
|
||||
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
|
||||
//it can be created either internally (via configuration) or externally (pointer to context).
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
|
||||
using bitsery::ext::BaseClass;
|
||||
@@ -32,7 +31,7 @@ struct Derive1:virtual Base {// virtually inherits from base
|
||||
};
|
||||
template <typename S>
|
||||
void serialize(S& s, Derive1& o) {
|
||||
//define virtual inheritance, it will not compile if InheritanceContext is not defined in serializer/deserialzer
|
||||
//define virtual inheritance, it will not compile if InheritanceContext is not defined in serializer/deserializer
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value1b(o.y1);
|
||||
}
|
||||
@@ -81,18 +80,11 @@ namespace bitsery {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
// since in this example we're using virtual inheritance we need InheritanceContext as well.
|
||||
// InheritanceContext is default constructable and has no useful information outside of serializer/deserializer
|
||||
// lets create it internally, via configuration
|
||||
struct ConfigWithContext:DefaultConfig {
|
||||
//always add internal contexts to tuple
|
||||
using InternalContext = std::tuple<ext::InheritanceContext>;
|
||||
};
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, ConfigWithContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, ConfigWithContext>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, ext::InheritanceContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, ext::InheritanceContext>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -103,15 +95,20 @@ int main() {
|
||||
|
||||
Buffer buf{};
|
||||
|
||||
BasicSerializer<Writer> ser{OutputBufferAdapter<Buffer>{buf}, nullptr};
|
||||
ext::InheritanceContext ctx1;
|
||||
Writer writer{buf, ctx1};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.object(data);
|
||||
auto writtenSize = AdapterAccess::getWriter(ser).writtenBytesCount();
|
||||
writer.flush();
|
||||
|
||||
|
||||
MultipleInheritance res{0};
|
||||
BasicDeserializer<Reader> des{InputBufferAdapter<Buffer>{buf.begin(), writtenSize}};
|
||||
ext::InheritanceContext ctx2;
|
||||
Reader reader{{buf.begin(), writer.writtenBytesCount()}, ctx2};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
des.object(res);
|
||||
assert(AdapterAccess::getReader(des).error() == ReaderError::NoError && AdapterAccess::getReader(des).isCompletedSuccessfully());
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
|
||||
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
|
||||
assert(writtenSize == 4);//base is serialized once, because it is inherited virtually
|
||||
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 OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig>;
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -48,17 +48,18 @@ int main() {
|
||||
//create buffer
|
||||
Buffer buffer{};
|
||||
|
||||
//create and serialize container, and get written bytes count
|
||||
Serializer<OutputAdapter> ser{OutputAdapter{buffer}};
|
||||
//create writer and serialize container
|
||||
Writer writer{buffer};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.container(data, 10);
|
||||
auto writtenBytes = AdapterAccess::getWriter(ser).writtenBytesCount();
|
||||
writer.flush();
|
||||
|
||||
//create and deserialize container
|
||||
Deserializer<InputAdapter> des{InputAdapter{buffer.begin(), writtenBytes}};
|
||||
//create reader and deserialize container
|
||||
Reader reader{{buffer.begin(), writer.writtenBytesCount()}};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
des.container(res, 10);
|
||||
|
||||
//check if everything went ok
|
||||
auto& reader = AdapterAccess::getReader(des);
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
assert(res == data);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ private:
|
||||
s.value4b(i1);
|
||||
|
||||
//set container elements to be candidates for non-owning pointers
|
||||
s.container(vdata, 100, [&s](MyStruct& d){
|
||||
s.container(vdata, 100, [](S& s, MyStruct& d){
|
||||
s.ext(d, ReferencedByPointer{});
|
||||
});
|
||||
//contains non owning pointers
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
//
|
||||
//you can also serialize non owning pointers first, pointer linking context will keep track on them
|
||||
//and as soon as pointer owner data is deserialized, all non-owning pointers will be updated
|
||||
s.container(vptr, 100, [&s](MyStruct* (&d)){
|
||||
s.container(vptr, 100, [](S& s, MyStruct* (&d)){
|
||||
s.ext(d, PointerObserver{});
|
||||
});
|
||||
//observer
|
||||
@@ -86,13 +86,12 @@ using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
//we will need PointerLinkingContext to work with pointers
|
||||
//so lets define our serializer/deserializer
|
||||
//if we need context for our own custom flow, we can define it as tuple like this:
|
||||
//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 MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, ext::PointerLinkingContext>;
|
||||
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, ext::PointerLinkingContext>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, ext::PointerLinkingContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, ext::PointerLinkingContext>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
@@ -115,16 +114,15 @@ 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 serializer/deserializer
|
||||
//in order to use pointers, we need to pass pointer linking context to writer/reader
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
//pass lining context to serializer, by pointer
|
||||
MySerializer ser{OutputAdapter{buffer}, &ctx};
|
||||
Writer writer{buffer, ctx};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
//serialize our data
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
writtenSize = w.writtenBytesCount();
|
||||
writer.flush();
|
||||
writtenSize = writer.writtenBytesCount();
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
//this ensures that all non-owning pointers points to data that has been serialized,
|
||||
@@ -135,13 +133,13 @@ int main() {
|
||||
Test1Data res{};
|
||||
{
|
||||
ext::PointerLinkingContext ctx{};
|
||||
//pass lining context to deserializer, by pointer
|
||||
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
//pass lining context to reader
|
||||
Reader reader{{buffer.begin(), writtenSize}, ctx};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
//deserialize our data
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
//check if everything went find
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(ctx.isValid());
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ void serialize(S &s, SomeShapes &o) {
|
||||
// bitsery will work regardless
|
||||
s.ext(o.weakPtr, StdSmartPtr{});
|
||||
s.ext(o.refPtr, PointerObserver{});
|
||||
s.container(o.sharedList, 100, [&s](std::shared_ptr<Shape> &item) {
|
||||
s.container(o.sharedList, 100, [](S& s, std::shared_ptr<Shape> &item) {
|
||||
s.ext(item, StdSmartPtr{});
|
||||
});
|
||||
}
|
||||
@@ -197,8 +197,8 @@ using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<
|
||||
//NOTE:
|
||||
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
|
||||
|
||||
using MySerializer = BasicSerializer<AdapterWriter<OutputAdapter, DefaultConfig>, TContext>;
|
||||
using MyDeserializer = BasicDeserializer<AdapterReader<InputAdapter, DefaultConfig>, TContext>;
|
||||
using Writer = AdapterWriter<OutputBufferAdapter<Buffer>, DefaultConfig, TContext>;
|
||||
using Reader = AdapterReader<InputBufferAdapter<Buffer>, DefaultConfig, TContext>;
|
||||
|
||||
|
||||
//checks if deserialized data is equal
|
||||
@@ -233,17 +233,19 @@ int main() {
|
||||
Buffer buffer{};
|
||||
size_t writtenSize{};
|
||||
{
|
||||
|
||||
//STEP 2
|
||||
//bind serializer with base polymorphic types, it will go through all reachable classes that is defined in first step.
|
||||
//so you dont need to add Rectangle to reach for RoundedRectangle
|
||||
// before start serialization/deserialization,
|
||||
// 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<MySerializer>(MyPolymorphicClassesForRegistering{});
|
||||
//serialize our data
|
||||
MySerializer ser{OutputAdapter{buffer}, &ctx};
|
||||
std::get<1>(ctx).registerBasesList<BasicSerializer<Writer>>(MyPolymorphicClassesForRegistering{});
|
||||
//create writer and serialize
|
||||
Writer writer{buffer, ctx};
|
||||
BasicSerializer<Writer> ser{writer};
|
||||
ser.object(data);
|
||||
auto &w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
writtenSize = w.writtenBytesCount();
|
||||
writer.flush();
|
||||
writtenSize = writer.writtenBytesCount();
|
||||
|
||||
//make sure that pointer linking context is valid
|
||||
//this ensures that all non-owning pointers points to data that has been serialized,
|
||||
@@ -253,13 +255,13 @@ int main() {
|
||||
SomeShapes res{};
|
||||
{
|
||||
TContext ctx{};
|
||||
std::get<1>(ctx).registerBasesList<MyDeserializer>(MyPolymorphicClassesForRegistering{});
|
||||
std::get<1>(ctx).registerBasesList<BasicDeserializer<Reader>>(MyPolymorphicClassesForRegistering{});
|
||||
//serialize our data
|
||||
MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx};
|
||||
Reader reader {{buffer.begin(), writtenSize}, ctx};
|
||||
BasicDeserializer<Reader> des{reader};
|
||||
des.object(res);
|
||||
auto &r = AdapterAccess::getReader(des);
|
||||
//check if everything went find
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully());
|
||||
//also check for dangling pointers, after deserialization
|
||||
assert(std::get<0>(ctx).isValid());
|
||||
// clear shared state from pointer linking context,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//base class that stores container iterators, and is required for session support (for reading sessions data)
|
||||
|
||||
template<typename Buffer>
|
||||
class BufferIterators {
|
||||
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
|
||||
@@ -42,12 +42,12 @@ namespace bitsery {
|
||||
static_assert(details::IsDefined<TIterator>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
BufferIterators(TIterator begin, TIterator end)
|
||||
: posIt{begin},
|
||||
: beginIt{begin},
|
||||
posIt{begin},
|
||||
endIt{end} {
|
||||
}
|
||||
|
||||
friend details::SessionAccess;
|
||||
|
||||
TIterator beginIt;
|
||||
TIterator posIt;
|
||||
TIterator endIt;
|
||||
};
|
||||
@@ -63,48 +63,84 @@ namespace bitsery {
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator endIt)
|
||||
: BufferIterators<Buffer>(begin, endIt) {
|
||||
: BufferIterators<Buffer>(begin, endIt),
|
||||
_endReadPos{endIt} {
|
||||
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)) {
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)),
|
||||
_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, this->endIt) >= 0) {
|
||||
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 (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
if (_overflowOnReadEndPos)
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return static_cast<size_t>(std::distance(this->beginIt, this->posIt));
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
const auto buffSize = static_cast<size_t>(std::distance(this->beginIt, this->endIt));
|
||||
if (buffSize >= pos) {
|
||||
_overflowOnReadEndPos = pos == 0;
|
||||
if (pos == 0)
|
||||
pos = buffSize;
|
||||
_endReadPos = std::next(this->beginIt, pos);
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
if (_overflowOnReadEndPos)
|
||||
return 0;
|
||||
return static_cast<size_t>(std::distance(this->beginIt, _endReadPos));
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto res = std::distance(this->endIt, this->posIt);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<ReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return ReaderError::NoError;
|
||||
return _err;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
this->endIt = this->posIt;
|
||||
//to avoid creating temporary for error state, mark an error by passing posIt after the endIt
|
||||
std::advance(this->posIt, static_cast<size_t>(error));
|
||||
void error(ReaderError error) {
|
||||
if (_err == ReaderError::NoError) {
|
||||
_err = error;
|
||||
_endReadPos = this->endIt;
|
||||
this->beginIt = this->endIt;
|
||||
this->posIt = this->endIt;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt;
|
||||
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:
|
||||
@@ -131,20 +167,42 @@ namespace bitsery {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return err;
|
||||
void currentReadPos(size_t pos) {
|
||||
if (std::distance(this->beginIt, this->endIt) >= pos) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
err = error;
|
||||
size_t currentReadPos() const {
|
||||
return static_cast<size_t>(std::distance(this->beginIt, this->posIt));
|
||||
}
|
||||
|
||||
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;
|
||||
return this->posIt == this->endIt && _err == ReaderError::NoError;
|
||||
}
|
||||
|
||||
private:
|
||||
ReaderError err = ReaderError::NoError;
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
@@ -169,12 +227,26 @@ namespace bitsery {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
const auto currPos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
const auto maxPos = currPos > pos ? currPos : pos;
|
||||
if (maxPos > _biggestCurrentPos) {
|
||||
_biggestCurrentPos = maxPos;
|
||||
}
|
||||
setCurrentWritePos(pos, TResizable{});
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return static_cast<size_t> (std::distance(std::begin(*_buffer), _outIt));
|
||||
}
|
||||
|
||||
void flush() {
|
||||
//this function might be useful for stream adapters
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
const auto pos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
return pos > _biggestCurrentPos ? pos : _biggestCurrentPos;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -183,6 +255,7 @@ namespace bitsery {
|
||||
Buffer *_buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
size_t _biggestCurrentPos{};
|
||||
|
||||
/*
|
||||
* resizable buffer
|
||||
@@ -228,6 +301,16 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
void setCurrentWritePos(size_t pos, std::true_type) {
|
||||
const auto begin = std::begin(*_buffer);
|
||||
if (static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos) {
|
||||
_outIt = std::next(begin, pos);
|
||||
} else {
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
setCurrentWritePos(pos, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* non resizable buffer
|
||||
*/
|
||||
@@ -243,6 +326,12 @@ namespace bitsery {
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
|
||||
void setCurrentWritePos(size_t pos, std::false_type) {
|
||||
const auto begin = std::begin(*_buffer);
|
||||
assert(static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos);
|
||||
_outIt = std::next(begin, pos);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -33,47 +33,68 @@ namespace bitsery {
|
||||
class BasicInputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
:_ios{std::addressof(istream)} {}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
if (static_cast<size_t>(_ios->rdbuf()->sgetn( data , size )) != size) {
|
||||
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn( data , size )) != _zeroIfNoErrors) {
|
||||
*data = {};
|
||||
//check state, if not set by stream, set it manually
|
||||
if (_ios->good())
|
||||
_ios->setstate(std::ios_base::eofbit);
|
||||
if (_zeroIfNoErrors == 0) {
|
||||
error(_ios->rdstate() == std::ios_base::badbit
|
||||
? ReaderError::ReadingError
|
||||
: ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
if (_ios->good())
|
||||
return ReaderError::NoError;
|
||||
return _ios->eof()
|
||||
? ReaderError::DataOverflow
|
||||
: ReaderError::ReadingError;
|
||||
void currentReadPos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
size_t currentReadEndPos() const {
|
||||
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _err;
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
if (error() == ReaderError::NoError) {
|
||||
return _ios->rdbuf()->sgetc() == CharTraits::eof();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void setError(ReaderError ) {
|
||||
//has no effect when using
|
||||
|
||||
void error(ReaderError error) {
|
||||
if (_err == ReaderError::NoError) {
|
||||
_err = error;
|
||||
_zeroIfNoErrors = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
size_t _zeroIfNoErrors{};
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicOutputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream)
|
||||
:_ios{std::addressof(ostream)} {}
|
||||
@@ -83,6 +104,15 @@ namespace bitsery {
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
void currentWritePos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
|
||||
ostream->flush();
|
||||
@@ -111,7 +141,6 @@ namespace bitsery {
|
||||
static_assert(details::IsDefined<BufferIt>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer for BasicBufferedOutputStreamAdapter");
|
||||
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BasicBufferedOutputStreamAdapter only works with contiguous containers");
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
//bufferSize is used when buffer is dynamically allocated
|
||||
BasicBufferedOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream, size_t bufferSize = 256)
|
||||
@@ -168,6 +197,15 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
void currentWritePos(size_t ) {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
|
||||
return {};
|
||||
}
|
||||
|
||||
void flush() {
|
||||
auto begin = std::begin(_buf);
|
||||
_adapter.write(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
|
||||
@@ -204,7 +242,6 @@ namespace bitsery {
|
||||
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, CharTraits>, public BasicOutputStreamAdapter<TChar, CharTraits> {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
//both bases contain reference to same iostream, so no need to do anything
|
||||
BasicIOStreamAdapter(std::basic_ios<TChar, CharTraits>& iostream)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#ifndef BITSERY_ADAPTER_READER_H
|
||||
#define BITSERY_ADAPTER_READER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
#include "details/adapter_common.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
@@ -32,50 +32,29 @@ namespace bitsery {
|
||||
template <typename TReader>
|
||||
class AdapterReaderBitPackingWrapper;
|
||||
|
||||
template<typename InputAdapter, typename Config>
|
||||
struct AdapterReader {
|
||||
//this is required by deserializer
|
||||
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;
|
||||
using TConfig = Config;
|
||||
using TValue = typename InputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
|
||||
explicit AdapterReader(InputAdapter&& adapter)
|
||||
: _inputAdapter{std::move(adapter)},
|
||||
_session{*this, _inputAdapter}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReader(const AdapterReader &) = delete;
|
||||
|
||||
AdapterReader &operator=(const AdapterReader &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterReader(AdapterReader &&) = default;
|
||||
|
||||
AdapterReader &operator=(AdapterReader &&) = default;
|
||||
|
||||
~AdapterReader() noexcept = default;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
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) {
|
||||
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 ) {
|
||||
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.");
|
||||
}
|
||||
@@ -83,47 +62,42 @@ namespace bitsery {
|
||||
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 _inputAdapter.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
return this->_adapter.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto err = _inputAdapter.error();
|
||||
if (err == ReaderError::DataOverflow && _session.hasActiveSessions())
|
||||
return ReaderError::NoError;
|
||||
return err;
|
||||
return this->_adapter.error();
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
if (this->error() == ReaderError::NoError)
|
||||
_inputAdapter.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.end();
|
||||
}
|
||||
void error(ReaderError error) {
|
||||
this->_adapter.error(error);
|
||||
}
|
||||
|
||||
using typename details::AdapterAndContext<InputAdapter, Config, Context>::TValue;
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
InputAdapter _inputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
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()>{});
|
||||
@@ -142,25 +116,12 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename TReader>
|
||||
class AdapterReaderBitPackingWrapper {
|
||||
class AdapterReaderBitPackingWrapper: public details::AdapterAndContextWrapper<TReader> {
|
||||
public:
|
||||
//this is required by deserializer
|
||||
|
||||
using details::AdapterAndContextWrapper<TReader>::AdapterAndContextWrapper;
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TReader::TConfig;
|
||||
//make TValue unsigned for bitpacking
|
||||
using UnsignedValue = typename std::make_unsigned<typename TReader::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit AdapterReaderBitPackingWrapper(TReader& reader):_reader{reader}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReaderBitPackingWrapper(const AdapterReaderBitPackingWrapper&) = delete;
|
||||
AdapterReaderBitPackingWrapper& operator = (const AdapterReaderBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterReaderBitPackingWrapper(AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterReaderBitPackingWrapper& operator = (AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterReaderBitPackingWrapper() {
|
||||
align();
|
||||
@@ -172,7 +133,7 @@ namespace bitsery {
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
_reader.template readBytes<SIZE,T>(v);
|
||||
this->_wrapped.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
@@ -183,7 +144,7 @@ namespace bitsery {
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
_reader.template readBuffer<SIZE,T>(buf, count);
|
||||
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
@@ -204,34 +165,43 @@ namespace bitsery {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(ReaderError::InvalidData);
|
||||
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 _reader.isCompletedSuccessfully();
|
||||
return this->_wrapped.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _reader.error();
|
||||
return this->_wrapped.error();
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
_reader.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_reader.beginSession();
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_reader.endSession();
|
||||
void error(ReaderError error) {
|
||||
this->_wrapped.error(error);
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
using UnsignedValue = typename std::make_unsigned<typename TReader::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
@@ -243,7 +213,7 @@ namespace bitsery {
|
||||
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
_reader.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BitsSize<UnsignedValue>::value;
|
||||
}
|
||||
|
||||
@@ -23,75 +23,109 @@
|
||||
#ifndef BITSERY_ADAPTER_WRITER_H
|
||||
#define BITSERY_ADAPTER_WRITER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
#include "details/adapter_common.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Config>
|
||||
struct BasicMeasureSize {
|
||||
//measure class is bit-packing enabled, no need to create wrapper for it
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
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, "");
|
||||
_bitsCount += details::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
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);
|
||||
_bitsCount += bitsCount;
|
||||
_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, "");
|
||||
_bitsCount += details::BitsSize<T>::value * count;
|
||||
_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 = (_bitsCount % 8);
|
||||
_bitsCount += (8 - _scratch) % 8;
|
||||
auto _scratch = (_currPosBits % 8);
|
||||
_currPosBits += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
//flush sessions count
|
||||
if (_sessionsBytesCount > 0) {
|
||||
_bitsCount += (_sessionsBytesCount + 4) * 8;
|
||||
_sessionsBytesCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
auto endPos = writtenBytesCount();
|
||||
details::writeSize(*this, endPos);
|
||||
auto sessionEndBytesCount = writtenBytesCount() - endPos;
|
||||
//remove written bytes, because we'll write them at the end
|
||||
_bitsCount -= sessionEndBytesCount * 8;
|
||||
_sessionsBytesCount += sessionEndBytesCount;
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t writtenBytesCount() const {
|
||||
return _bitsCount / 8;
|
||||
const auto max = _currPosBits > _prevLargestPos ? _currPosBits : _prevLargestPos;
|
||||
return max / 8;
|
||||
}
|
||||
|
||||
ExternalContext& externalContext() {
|
||||
return _externalContext;
|
||||
}
|
||||
|
||||
InternalContext& internalContext() {
|
||||
return _internalContext;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _bitsCount{};
|
||||
size_t _sessionsBytesCount{};
|
||||
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
|
||||
@@ -100,28 +134,13 @@ namespace bitsery {
|
||||
template <typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
template<typename OutputAdapter, typename Config>
|
||||
struct AdapterWriter {
|
||||
//this is required by serializer
|
||||
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 TConfig = Config;
|
||||
using TValue = typename OutputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
explicit AdapterWriter(OutputAdapter&& adapter)
|
||||
: _outputAdapter{std::move(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriter(const AdapterWriter &) = delete;
|
||||
|
||||
AdapterWriter &operator=(const AdapterWriter &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterWriter(AdapterWriter &&) = default;
|
||||
|
||||
AdapterWriter &operator=(AdapterWriter &&) = default;
|
||||
using typename details::AdapterAndContext<OutputAdapter, Config, Context>::TValue;
|
||||
|
||||
~AdapterWriter() {
|
||||
flush();
|
||||
@@ -153,25 +172,24 @@ namespace bitsery {
|
||||
|
||||
}
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
this->_adapter.currentWritePos(pos);
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return this->_adapter.currentWritePos();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
_session.flushSessions(*this);
|
||||
_outputAdapter.flush();
|
||||
this->_adapter.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _outputAdapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
_session.begin(*this);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
_session.end(*this);
|
||||
return this->_adapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
@@ -182,45 +200,23 @@ namespace bitsery {
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
this->_adapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
this->_adapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
OutputAdapter _outputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsWriter<AdapterWriter<OutputAdapter, Config >>,
|
||||
session::DisabledSessionsWriter<AdapterWriter<OutputAdapter, Config>>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
//this class is used as wrapper for real AdapterWriter, it doesn't store writer itself just a reference
|
||||
template<typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper {
|
||||
class AdapterWriterBitPackingWrapper: public details::AdapterAndContextWrapper<TWriter> {
|
||||
public:
|
||||
//this is required by serializer
|
||||
using details::AdapterAndContextWrapper<TWriter>::AdapterAndContextWrapper;
|
||||
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
using TConfig = typename TWriter::TConfig;
|
||||
|
||||
//make TValue unsigned for bit packing
|
||||
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");
|
||||
|
||||
explicit AdapterWriterBitPackingWrapper(TWriter &writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriterBitPackingWrapper(const AdapterWriterBitPackingWrapper&) = delete;
|
||||
AdapterWriterBitPackingWrapper& operator = (const AdapterWriterBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterWriterBitPackingWrapper(AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterWriterBitPackingWrapper& operator = (AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterWriterBitPackingWrapper() {
|
||||
align();
|
||||
@@ -232,7 +228,7 @@ namespace bitsery {
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBytes<SIZE,T>(v);
|
||||
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);
|
||||
@@ -244,7 +240,7 @@ namespace bitsery {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBuffer<SIZE,T>(buf, count);
|
||||
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
@@ -268,27 +264,31 @@ namespace bitsery {
|
||||
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();
|
||||
_writer._session.flushSessions(_writer);
|
||||
this->_wrapped.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_writer._session.begin(_writer);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_writer._session.end(_writer);
|
||||
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;
|
||||
@@ -300,7 +300,7 @@ namespace bitsery {
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
@@ -317,7 +317,7 @@ namespace bitsery {
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
@@ -327,8 +327,6 @@ namespace bitsery {
|
||||
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
TWriter& _writer;
|
||||
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
@@ -40,9 +40,6 @@ namespace bitsery {
|
||||
struct DefaultConfig {
|
||||
//data will be stored in little endian, independant of host.
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
//this functionality allows to support backward/forward compatibility
|
||||
//however reading from streams is not supported, because this functionality requires random access to buffer.
|
||||
static constexpr bool BufferSessionsEnabled = false;
|
||||
//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.
|
||||
|
||||
@@ -31,50 +31,32 @@
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename TAdapterReader, typename TContext = void>
|
||||
template<typename TAdapterReader>
|
||||
class BasicDeserializer {
|
||||
public:
|
||||
//this is used by AdapterAccess class
|
||||
using TReader = TAdapterReader;
|
||||
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
|
||||
//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, TContext>;
|
||||
TAdapterReader,
|
||||
AdapterReaderBitPackingWrapper<TAdapterReader>>::type>;
|
||||
|
||||
static_assert(details::IsSpecializationOf<typename TReader::TConfig::InternalContext, std::tuple>::value,
|
||||
"Config::InternalContext must be std::tuple");
|
||||
|
||||
template <typename ReaderParam>
|
||||
explicit BasicDeserializer(ReaderParam&& r, TContext* context = nullptr)
|
||||
: _reader{std::forward<ReaderParam>(r)},
|
||||
_context{context},
|
||||
_internalContext{}
|
||||
explicit BasicDeserializer(TAdapterReader& reader)
|
||||
: _reader{reader}
|
||||
{
|
||||
}
|
||||
|
||||
//copying disabled
|
||||
BasicDeserializer(const BasicDeserializer&) = delete;
|
||||
BasicDeserializer& operator = (const BasicDeserializer&) = delete;
|
||||
|
||||
//move enabled
|
||||
BasicDeserializer(BasicDeserializer&& ) = default;
|
||||
BasicDeserializer& operator = (BasicDeserializer&& ) = default;
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific deserialization flows.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
TContext* context() {
|
||||
return _context;
|
||||
|
||||
template <typename T>
|
||||
T& context() {
|
||||
return *details::getContext<true, T>(_reader.context());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* context(){
|
||||
return details::getContext<T>(_context, _internalContext);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull(){
|
||||
return details::getContextIfTypeExists<T>(_context, _internalContext);
|
||||
T* contextOrNull() {
|
||||
return details::getContext<false, T>(_reader.context());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -88,7 +70,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void object(T &&obj, Fnc &&fnc) {
|
||||
fnc(std::forward<T>(obj));
|
||||
fnc(*this, std::forward<T>(obj));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -104,7 +86,9 @@ namespace bitsery {
|
||||
|
||||
template <typename T, typename... TArgs>
|
||||
BasicDeserializer &operator()(T &&head, TArgs &&... tail) {
|
||||
//serialize object
|
||||
details::ArchiveFunction<BasicDeserializer, T>::invoke(*this, std::forward<T>(head));
|
||||
//expand other elements
|
||||
archive(std::forward<TArgs>(tail)...);
|
||||
return *this;
|
||||
}
|
||||
@@ -146,7 +130,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, [this](VType &v) { value<VSIZE>(v);});
|
||||
extension.deserialize(*this, _reader, obj, [](BasicDeserializer& s, VType &v) { s.value<VSIZE>(v);});
|
||||
}
|
||||
|
||||
template<typename T, typename Ext>
|
||||
@@ -156,7 +140,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.deserialize(*this, _reader, obj, [this](VType &v) { object(v); });
|
||||
extension.deserialize(*this, _reader, obj, [](BasicDeserializer& s, VType &v) { s.object(v); });
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -264,10 +248,6 @@ namespace bitsery {
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
void align() {
|
||||
_reader.align();
|
||||
}
|
||||
|
||||
//overloads for functions with explicit type size
|
||||
|
||||
template<typename T>
|
||||
@@ -337,14 +317,8 @@ namespace bitsery {
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
private:
|
||||
friend AdapterAccess;
|
||||
// this is required when creating bitpacking serializer, to access internal context
|
||||
friend class BasicDeserializer<typename details::GetNonWrappedAdapterReader<TAdapterReader>::Reader, TContext>;
|
||||
|
||||
TAdapterReader _reader;
|
||||
TContext* _context;
|
||||
typename TReader::TConfig::InternalContext _internalContext;
|
||||
|
||||
TAdapterReader& _reader;
|
||||
|
||||
//process value types
|
||||
//false_type means that we must process all elements individually
|
||||
@@ -368,7 +342,7 @@ namespace bitsery {
|
||||
template<typename It, typename Fnc>
|
||||
void procContainer(It first, It last, Fnc fnc) {
|
||||
for (; first != last; ++first)
|
||||
fnc(*first);
|
||||
fnc(*this, *first);
|
||||
}
|
||||
|
||||
//process object types
|
||||
@@ -400,7 +374,7 @@ namespace bitsery {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
_reader.error(ReaderError::InvalidData);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
@@ -413,12 +387,10 @@ namespace bitsery {
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
BPEnabledType tmp(_reader, _context);
|
||||
// move internal context to and from of bitpacking enabled serializer
|
||||
tmp._internalContext = std::move(_internalContext);
|
||||
fnc(tmp);
|
||||
_internalContext = std::move(tmp._internalContext);
|
||||
//create deserializer using bitpacking wrapper
|
||||
AdapterReaderBitPackingWrapper<TAdapterReader> bitPackingWrapper{_reader};
|
||||
BPEnabledType deserializer{bitPackingWrapper};
|
||||
fnc(deserializer);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
@@ -438,16 +410,14 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//helper type
|
||||
template <typename Adapter>
|
||||
using Deserializer = BasicDeserializer<AdapterReader<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after deserialziation returns status
|
||||
template <typename Adapter, typename T>
|
||||
std::pair<ReaderError, bool> quickDeserialization(Adapter adapter, T& value) {
|
||||
Deserializer<Adapter> des{std::move(adapter)};
|
||||
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};
|
||||
des.object(value);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
return {r.error(), r.isCompletedSuccessfully()};
|
||||
return {reader.error(), reader.isCompletedSuccessfully()};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,15 @@ namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
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 {
|
||||
};
|
||||
|
||||
|
||||
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");
|
||||
@@ -107,20 +116,78 @@ namespace bitsery {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* class used by session reader, to access underlying iterators of buffer
|
||||
*/
|
||||
struct SessionAccess {
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& posIteratorRef(TReader& r) {
|
||||
return r.posIt;
|
||||
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;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
// 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{}
|
||||
{
|
||||
}
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& endIteratorRef(TReader& r) {
|
||||
return r.endIt;
|
||||
|
||||
// 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}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterAndContext(const AdapterAndContext &) = delete;
|
||||
AdapterAndContext &operator=(const AdapterAndContext &) = delete;
|
||||
|
||||
// todo conditionally noexcept
|
||||
AdapterAndContext(AdapterAndContext &&) = default;
|
||||
AdapterAndContext &operator=(AdapterAndContext &&) = default;
|
||||
|
||||
TContext& context() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
protected:
|
||||
Adapter _adapter;
|
||||
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}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterAndContextWrapper(const AdapterAndContextWrapper &) = delete;
|
||||
AdapterAndContextWrapper &operator=(const AdapterAndContextWrapper &) = delete;
|
||||
|
||||
AdapterAndContextWrapper(AdapterAndContextWrapper &&) noexcept = default;
|
||||
AdapterAndContextWrapper &operator=(AdapterAndContextWrapper &&) noexcept = default;
|
||||
|
||||
TContext& context() {
|
||||
return _wrapped.context();
|
||||
}
|
||||
|
||||
protected:
|
||||
AdapterAndCtx& _wrapped;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.setError(ReaderError::InvalidData);
|
||||
r.error(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include "adapter_utils.h"
|
||||
#include "adapter_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
|
||||
@@ -53,6 +53,38 @@ namespace bitsery {
|
||||
|
||||
};
|
||||
|
||||
|
||||
// convenient functors that can be passed as lambda to serializer/deserializer instead of writing lambda
|
||||
// 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>{});
|
||||
template<size_t N, typename Ext>
|
||||
struct FtorExtValue : public Ext {
|
||||
template <typename S, typename T>
|
||||
void operator()(S& s, T& v) const {
|
||||
s.template ext<N>(v, static_cast<const Ext&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ext>
|
||||
struct FtorExtValue1b: FtorExtValue<1, Ext> {};
|
||||
template <typename Ext>
|
||||
struct FtorExtValue2b: FtorExtValue<2, Ext> {};
|
||||
template <typename Ext>
|
||||
struct FtorExtValue4b: FtorExtValue<4, Ext> {};
|
||||
template <typename Ext>
|
||||
struct FtorExtValue8b: FtorExtValue<8, Ext> {};
|
||||
|
||||
template<typename Ext>
|
||||
struct FtorExtObject : public Ext {
|
||||
template <typename S, typename T>
|
||||
void operator()(S& s, T& v) const {
|
||||
s.ext(v, static_cast<const Ext&>(*this));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//when call to serialize function is ambiguous (member and non-member serialize function exists for a type)
|
||||
//specialize this class by inheriting from either UseNonMemberFnc or UseMemberFnc
|
||||
//e.g.
|
||||
@@ -67,23 +99,6 @@ namespace bitsery {
|
||||
struct UseMemberFnc : std::integral_constant<int, 2> {
|
||||
};
|
||||
|
||||
|
||||
//serializer/deserializer, does not have public interface to get underlying writer/reader
|
||||
//to prevent users from using writer/reader directly, because they have different interface
|
||||
//and they cannot be used describing serialization flows.: use extensions for this reason.
|
||||
//this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions.
|
||||
struct AdapterAccess {
|
||||
template<typename Serializer>
|
||||
static typename Serializer::TWriter &getWriter(Serializer &s) {
|
||||
return s._writer;
|
||||
}
|
||||
|
||||
template<typename Deserializer>
|
||||
static typename Deserializer::TReader &getReader(Deserializer &s) {
|
||||
return s._reader;
|
||||
}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
//helper types for error handling
|
||||
@@ -285,129 +300,72 @@ namespace bitsery {
|
||||
* helper function for getting context from serializer/deserializer
|
||||
*/
|
||||
|
||||
template<typename T, template<typename...> class Template>
|
||||
struct IsSpecializationOf : std::false_type {
|
||||
};
|
||||
template<int Index, typename... Conds>
|
||||
struct FindIndex : std::integral_constant<int, Index> {};
|
||||
|
||||
template<template<typename...> class Template, typename... Args>
|
||||
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
|
||||
};
|
||||
template<int Index, typename Cond, typename... Conds>
|
||||
struct FindIndex<Index, Cond, Conds...> :
|
||||
std::conditional<Cond::value, std::integral_constant<int, Index>, FindIndex<Index+1, Conds...>>::type
|
||||
{};
|
||||
|
||||
//helper types for better error messages
|
||||
template<typename Find, typename ... TList>
|
||||
struct GetTypeIndex : std::integral_constant<size_t, 0> {
|
||||
};
|
||||
template <typename T, typename Tuple>
|
||||
struct GetConvertibleTypeIndexFromTuple;
|
||||
|
||||
//found it
|
||||
template<typename Find, typename ... Tail>
|
||||
struct GetTypeIndex<Find, Find, Tail...> : std::integral_constant<size_t, 0> {
|
||||
};
|
||||
template <typename T, typename... Us>
|
||||
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>> : FindIndex<0, std::is_convertible<Us&, T&>...> {};
|
||||
|
||||
//iteratates over types
|
||||
template<typename Find, typename Head, typename ... Tail>
|
||||
struct GetTypeIndex<Find, Head, Tail...> : std::integral_constant<size_t,
|
||||
1 + GetTypeIndex<Find, Tail...>::value> {
|
||||
};
|
||||
|
||||
template<typename Find, typename ... TList>
|
||||
struct HasType : std::integral_constant<bool, (GetTypeIndex<Find, TList...>::value<(sizeof ... (TList)))> {
|
||||
};
|
||||
template <typename T, typename Tuple>
|
||||
struct IsExistsConvertibleTupleType;
|
||||
|
||||
template<typename TCast, typename Tuple>
|
||||
struct HasContext : std::is_same<TCast, Tuple> {
|
||||
};
|
||||
template <typename T, typename... Us>
|
||||
struct IsExistsConvertibleTupleType<T, std::tuple<Us...>> :
|
||||
std::integral_constant<bool, GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value != sizeof...(Us)> {};
|
||||
|
||||
template<typename TCast, typename ... Args>
|
||||
struct HasContext<TCast, std::tuple<Args...>> : HasType<TCast, Args...> {
|
||||
};
|
||||
|
||||
/*
|
||||
* get context, and static assert if type doesn't exists
|
||||
* get context from internal or external, and check if it's convertible or not
|
||||
*/
|
||||
|
||||
template<typename TCast, typename ... Args>
|
||||
TCast *getContextImpl(std::tuple<Args...> *ctx, std::true_type) {
|
||||
using TCastIndex = GetTypeIndex<TCast, Args...>;
|
||||
static_assert(HasType<TCast, Args...>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return std::addressof(std::get<TCastIndex::value>(*ctx));
|
||||
|
||||
template<bool AssertExists, typename TCast, typename TContext>
|
||||
TCast* getDirectlyIfExists(TContext& ctx, std::true_type) {
|
||||
return &static_cast<TCast&>(ctx);
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImpl(TContext *ctx, std::false_type) {
|
||||
static_assert(std::is_convertible<TContext *, TCast *>::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return static_cast<TCast *>(ctx);
|
||||
}
|
||||
|
||||
//get local ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContext(TContext *, TInternalContext &internalCtx, std::true_type) {
|
||||
return getContextImpl<TCast>(&internalCtx, std::true_type{});
|
||||
}
|
||||
|
||||
//get external ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContext(TContext *ctx, TInternalContext &, std::false_type) {
|
||||
return ctx
|
||||
? getContextImpl<TCast>(ctx, IsSpecializationOf<TContext, std::tuple>{})
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *getContext(TContext *ctx, TInternalContext &internalCtx) {
|
||||
return chooseInternalOrExternalContext<TCast>(ctx, internalCtx, HasContext<TCast, TInternalContext>{});
|
||||
}
|
||||
|
||||
/*
|
||||
* get context, if type doesn't exists then do not static_assert but return null instead
|
||||
*/
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTypeIfExists(TContext *ctx, std::true_type) {
|
||||
return static_cast<TCast *>(ctx);
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTypeIfExists(TContext *, std::false_type) {
|
||||
template<bool AssertExists, typename TCast, typename TContext>
|
||||
TCast* getDirectlyIfExists(TContext& , std::false_type) {
|
||||
// TCast cannot be convertible from provided context
|
||||
static_assert(!AssertExists,
|
||||
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImplIfExists(TContext *ctx, std::false_type) {
|
||||
return getContextFromTypeIfExists<TCast>(ctx, std::is_convertible<TContext *, TCast *>{});
|
||||
|
||||
template<bool AssertExists, typename TCast, typename ... TArgs>
|
||||
TCast* getFromTupleIfExists(std::tuple<TArgs...>& ctx, std::true_type) {
|
||||
using TupleIndex = GetConvertibleTypeIndexFromTuple<TCast, std::tuple<TArgs...>>;
|
||||
return &static_cast<TCast&>(std::get<TupleIndex::value>(ctx));
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTupleIfExists(TContext *ctx, std::true_type tmp) {
|
||||
return getContextImpl<TCast>(ctx, tmp);
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextFromTupleIfExists(TContext *, std::false_type) {
|
||||
template<bool AssertExists, typename TCast, typename ... TArgs>
|
||||
TCast* getFromTupleIfExists(std::tuple<TArgs...>& , std::false_type) {
|
||||
// TCast cannot be convertible from provided context
|
||||
static_assert(!AssertExists,
|
||||
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext>
|
||||
TCast *getContextImplIfExists(TContext *ctx, std::true_type) {
|
||||
return getContextFromTupleIfExists<TCast>(ctx, HasContext<TCast, TContext>{});
|
||||
//non tuple context
|
||||
template<bool AssertExists, typename TCast, typename TContext>
|
||||
TCast* getContext(TContext& ctx) {
|
||||
return getDirectlyIfExists<AssertExists, TCast>(ctx, std::is_convertible<TContext&, TCast&>{});
|
||||
}
|
||||
|
||||
//get local ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContextIfExists(TContext *, TInternalContext &internalCtx, std::true_type) {
|
||||
return getContextImplIfExists<TCast>(&internalCtx, std::true_type{});
|
||||
}
|
||||
|
||||
//get external ctx
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *chooseInternalOrExternalContextIfExists(TContext *ctx, TInternalContext &, std::false_type) {
|
||||
return ctx
|
||||
? getContextImplIfExists<TCast>(ctx, IsSpecializationOf<TContext, std::tuple>{})
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
template<typename TCast, typename TContext, typename TInternalContext>
|
||||
TCast *getContextIfTypeExists(TContext *ctx, TInternalContext &internalCtx) {
|
||||
return chooseInternalOrExternalContextIfExists<TCast>(ctx, internalCtx, HasContext<TCast, TInternalContext>{});
|
||||
//tuple context
|
||||
template<bool AssertExists, typename TCast, typename ... TArgs>
|
||||
TCast* getContext(std::tuple<TArgs...>& ctx) {
|
||||
return getFromTupleIfExists<AssertExists, TCast>(ctx, IsExistsConvertibleTupleType<TCast, std::tuple<TArgs...>>{});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2018 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_SESSIONS_H
|
||||
#define BITSERY_DETAILS_SESSIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include "adapter_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace session {
|
||||
/*
|
||||
* writer/reader implementations that disable session support
|
||||
*/
|
||||
template <typename TWriter>
|
||||
struct DisabledSessionsWriter {
|
||||
void begin(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void end(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct DisabledSessionsReader {
|
||||
template <typename TBufferContext>
|
||||
DisabledSessionsReader(TReader& , TBufferContext& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* writer/reader real implementations
|
||||
* sessions reading requires to have random access iterators, so it cannot be used with streams
|
||||
*/
|
||||
template <typename TWriter>
|
||||
class SessionsWriter {
|
||||
public:
|
||||
|
||||
SessionsWriter() = default;
|
||||
SessionsWriter(const SessionsWriter&) = delete;
|
||||
SessionsWriter& operator = (const SessionsWriter& ) = delete;
|
||||
|
||||
SessionsWriter(SessionsWriter&&) = default;
|
||||
SessionsWriter& operator = (SessionsWriter&& ) = default;
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
auto sessionSize = writer.writtenBytesCount();
|
||||
assert(sessionSize > 0);
|
||||
*sessionIt = sessionSize;
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto dataSize = writer.writtenBytesCount();
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
auto totalSize = writer.writtenBytesCount();
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 4;//4 bytes for offset data
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(sessionsOffset));
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex{};
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct SessionsReader {
|
||||
using TIterator = typename TReader::TIterator;
|
||||
|
||||
template <typename InputAdapter>
|
||||
SessionsReader(TReader& r, InputAdapter& adapter)
|
||||
:_reader{r},
|
||||
_beginIt{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_posItRef{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_endItRef{details::SessionAccess::endIteratorRef<InputAdapter, TIterator>(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
SessionsReader(const SessionsReader&) = delete;
|
||||
SessionsReader& operator = (const SessionsReader& ) = delete;
|
||||
|
||||
SessionsReader(SessionsReader&&) = default;
|
||||
SessionsReader& operator = (SessionsReader&& ) = default;
|
||||
|
||||
void begin() {
|
||||
if (_sessions.empty()) {
|
||||
if (!initializeSessions())
|
||||
return;
|
||||
}
|
||||
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_endItRef);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_posItRef, _endItRef) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_beginIt, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _endItRef) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return;
|
||||
}
|
||||
_endItRef = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_posItRef == _endItRef || _reader.error() == ReaderError::NoError) || !(_sessionsStack.size() > 1)) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//error() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_posItRef, _endItRef);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_beginIt, _endItRef));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
//modify pointers only if no error or buffer overflow
|
||||
if (_reader.error() == ReaderError::NoError || _reader.error() == ReaderError::DataOverflow) {
|
||||
_posItRef = _endItRef;
|
||||
//restore end position
|
||||
_endItRef = _sessionsStack.top();
|
||||
}
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _beginIt;
|
||||
TIterator& _posItRef;
|
||||
TIterator& _endItRef;
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
bool initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _posItRef;
|
||||
//read size
|
||||
if (std::distance(_posItRef, _endItRef) < 4) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_endItRef, -4);
|
||||
_posItRef = endSessionsSizesIt;
|
||||
uint32_t sessionsOffset{};
|
||||
_reader.template readBytes<4>(sessionsOffset);
|
||||
|
||||
auto bufferSize = std::distance(_beginIt, _endItRef);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_posItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
while (std::distance(_posItRef, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_posItRef = currPos;
|
||||
_endItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_SESSIONS_H
|
||||
@@ -125,7 +125,7 @@ namespace bitsery {
|
||||
void checkReadOverflow(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.setError(bitsery::ReaderError::DataOverflow);
|
||||
r.error(bitsery::ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,27 +57,27 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
void serialize(Ser &s, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
auto index = details::findEntropyIndex(obj, _values);
|
||||
s.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
s.align();
|
||||
writer.align();
|
||||
if (!index)
|
||||
fnc(const_cast<T &>(obj));
|
||||
fnc(s, const_cast<T &>(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &, T &obj, Fnc &&fnc) const {
|
||||
void deserialize(Des &d, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
size_t index{};
|
||||
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
d.align();
|
||||
reader.align();
|
||||
if (index)
|
||||
obj = *std::next(std::begin(_values), index-1);
|
||||
else
|
||||
fnc(obj);
|
||||
fnc(d, obj);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -30,23 +30,38 @@ namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
/*
|
||||
* enables to add additional serialization methods at the end of method, without breaking existing older code
|
||||
* enables forward and backward compatibility, by allowing to append additional data at the end of serialization
|
||||
* old deserialization method will ignore additional data by jumping through it at the end of deserialization flow
|
||||
* new deserialization method will read all 0 for new fields if there is no data for it
|
||||
*/
|
||||
class Growable {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
writer.beginSession();
|
||||
fnc(const_cast<T&>(obj));
|
||||
writer.endSession();
|
||||
void serialize(Ser &ser, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
const auto startPos = writer.currentWritePos();
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(0));
|
||||
|
||||
fnc(ser, const_cast<T&>(obj));
|
||||
|
||||
const auto endPos = writer.currentWritePos();
|
||||
writer.currentWritePos(startPos);
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(endPos - startPos));
|
||||
writer.currentWritePos(endPos);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
reader.beginSession();
|
||||
fnc(obj);
|
||||
reader.endSession();
|
||||
void deserialize(Des &des, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
uint32_t size{};
|
||||
const auto readEndPos = reader.currentReadEndPos();
|
||||
const auto startPos = reader.currentReadPos();
|
||||
reader.template readBytes<4>(size);
|
||||
reader.currentReadEndPos(startPos + size);
|
||||
|
||||
fnc(des, obj);
|
||||
|
||||
reader.currentReadPos(startPos + size);
|
||||
reader.currentReadEndPos(readEndPos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <unordered_set>
|
||||
#include "../traits/core/traits.h"
|
||||
#include "utils/memory_allocator.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
@@ -34,7 +35,9 @@ namespace bitsery {
|
||||
//for standard inheritance (ext::BaseClass) it is optional.
|
||||
class InheritanceContext {
|
||||
public:
|
||||
InheritanceContext() = default;
|
||||
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
|
||||
:_virtualBases{pointer_utils::PolymorphicAllocatorWrapper<const void*>{memResource}}
|
||||
{}
|
||||
InheritanceContext(const InheritanceContext&) = delete;
|
||||
InheritanceContext&operator = (const InheritanceContext&) = delete;
|
||||
InheritanceContext(InheritanceContext&&) = default;
|
||||
@@ -66,7 +69,10 @@ namespace bitsery {
|
||||
size_t _depth{};
|
||||
const void* _parentPtr{};
|
||||
//add virtual bases to the list, as long as we're on the same parent
|
||||
std::unordered_set<const void*> _virtualBases{};
|
||||
std::unordered_set<const void*,
|
||||
std::hash<const void*>, std::equal_to<const void*>,
|
||||
pointer_utils::PolymorphicAllocatorWrapper<const void*>
|
||||
> _virtualBases;
|
||||
};
|
||||
|
||||
template <typename TBase>
|
||||
@@ -78,10 +84,10 @@ namespace bitsery {
|
||||
auto& resObj = static_cast<const TBase&>(obj);
|
||||
if (auto ctx = ser.template contextOrNull<InheritanceContext>()) {
|
||||
ctx->beginBase(obj, resObj);
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +96,10 @@ namespace bitsery {
|
||||
auto& resObj = static_cast<TBase&>(obj);
|
||||
if (auto ctx = des.template contextOrNull<InheritanceContext>()) {
|
||||
ctx->beginBase(obj, resObj);
|
||||
fnc(resObj);
|
||||
fnc(des, resObj);
|
||||
ctx->end();
|
||||
} else {
|
||||
fnc(resObj);
|
||||
fnc(des, resObj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,20 +112,20 @@ namespace bitsery {
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
auto ctx = ser.template context<InheritanceContext>();
|
||||
auto& ctx = ser.template context<InheritanceContext>();
|
||||
auto& resObj = static_cast<const TBase&>(obj);
|
||||
if (ctx->beginVirtualBase(obj, resObj))
|
||||
fnc(const_cast<TBase&>(resObj));
|
||||
ctx->end();
|
||||
if (ctx.beginVirtualBase(obj, resObj))
|
||||
fnc(ser, const_cast<TBase&>(resObj));
|
||||
ctx.end();
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
|
||||
auto ctx = des.template context<InheritanceContext>();
|
||||
auto& ctx = des.template context<InheritanceContext>();
|
||||
auto& resObj = static_cast<TBase&>(obj);
|
||||
if (ctx->beginVirtualBase(obj, resObj))
|
||||
fnc(resObj);
|
||||
ctx->end();
|
||||
if (ctx.beginVirtualBase(obj, resObj))
|
||||
fnc(des, resObj);
|
||||
ctx.end();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -49,21 +49,21 @@ namespace bitsery {
|
||||
return PointerOwnershipType::Owner;
|
||||
}
|
||||
|
||||
static void create(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
obj = alloc.allocate<TElement>(typeId);
|
||||
static void create(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) {
|
||||
obj = alloc.newObject<TElement>(typeId);
|
||||
}
|
||||
|
||||
static void createPolymorphic(T& obj, PolymorphicAllocator& alloc,
|
||||
static void createPolymorphic(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
obj = static_cast<TElement*>(handler->create(alloc));
|
||||
}
|
||||
|
||||
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
alloc.deallocate(obj, typeId);
|
||||
static void destroy(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) {
|
||||
alloc.deleteObject(obj, typeId);
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc,
|
||||
static void destroyPolymorphic(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
handler->destroy(alloc, obj);
|
||||
obj = nullptr;
|
||||
@@ -91,11 +91,11 @@ namespace bitsery {
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
static void destroy(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& , size_t ) {
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
|
||||
static void destroyPolymorphic(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& , PolymorphicHandlerBase& ) {
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
@@ -119,19 +119,19 @@ namespace bitsery {
|
||||
// this code is unreachable for reference type, but is necessary to compile
|
||||
// LCOV_EXCL_START
|
||||
|
||||
static void create(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
static void create(T& , pointer_utils::PolymorphicAllocatorWithTypeId& , size_t ) {
|
||||
|
||||
}
|
||||
|
||||
static void createPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
|
||||
static void createPolymorphic(T& , pointer_utils::PolymorphicAllocatorWithTypeId& , PolymorphicHandlerBase& ) {
|
||||
|
||||
}
|
||||
|
||||
static void destroy(T& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
static void destroy(T& , pointer_utils::PolymorphicAllocatorWithTypeId& , size_t ) {
|
||||
|
||||
}
|
||||
|
||||
static void destroyPolymorphic(T& obj, PolymorphicAllocator& alloc, PolymorphicHandlerBase& handler) {
|
||||
static void destroyPolymorphic(T& , pointer_utils::PolymorphicAllocatorWithTypeId& , PolymorphicHandlerBase& ) {
|
||||
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -33,15 +33,15 @@ namespace bitsery {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Period, typename Fnc>
|
||||
void serialize(Ser&, Writer&, const std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
|
||||
void serialize(Ser& ser, Writer&, const std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
|
||||
auto res = obj.count();
|
||||
fnc(res);
|
||||
fnc(ser, res);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Period, typename Fnc>
|
||||
void deserialize(Des&, Reader&, std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
|
||||
void deserialize(Des& des, Reader&, std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
|
||||
T res{};
|
||||
fnc(res);
|
||||
fnc(des, res);
|
||||
obj = std::chrono::duration<T, Period>{res};
|
||||
}
|
||||
};
|
||||
@@ -50,17 +50,17 @@ namespace bitsery {
|
||||
public:
|
||||
|
||||
template<typename Ser, typename Writer, typename Clock, typename T, typename Period, typename Fnc>
|
||||
void serialize(Ser&, Writer&, const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
|
||||
void serialize(Ser& ser, Writer&, const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
|
||||
Fnc&& fnc) const {
|
||||
auto res = obj.time_since_epoch().count();
|
||||
fnc(res);
|
||||
fnc(ser, res);
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename Clock, typename T, typename Period, typename Fnc>
|
||||
void deserialize(Des&, Reader&, std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
|
||||
void deserialize(Des& des, Reader&, std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
|
||||
Fnc&& fnc) const {
|
||||
T res{};
|
||||
fnc(res);
|
||||
fnc(des, res);
|
||||
auto dur = std::chrono::duration<T, Period>{res};
|
||||
obj = std::chrono::time_point<Clock, std::chrono::duration<T, Period>>{dur};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace bitsery {
|
||||
constexpr explicit StdMap(size_t maxSize):_maxSize{maxSize} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
void serialize(Ser &ser, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
using TValue = typename T::mapped_type;
|
||||
auto size = obj.size();
|
||||
@@ -46,11 +46,11 @@ namespace bitsery {
|
||||
details::writeSize(writer, size);
|
||||
|
||||
for (auto &v:obj)
|
||||
fnc(const_cast<TKey &>(v.first), const_cast<TValue &>(v.second));
|
||||
fnc(ser, const_cast<TKey &>(v.first), const_cast<TValue &>(v.second));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
void deserialize(Des &des, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
using TValue = typename T::mapped_type;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace bitsery {
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
auto value{bitsery::Access::create<TValue>()};
|
||||
fnc(key, value);
|
||||
fnc(des, key, value);
|
||||
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,23 +41,23 @@ namespace bitsery {
|
||||
explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::optional<T> &obj, Fnc &&fnc) const {
|
||||
void serialize(Ser &ser, Writer &writer, const std::optional<T> &obj, Fnc &&fnc) const {
|
||||
ser.boolValue(static_cast<bool>(obj));
|
||||
if (_alignBeforeData)
|
||||
ser.align();
|
||||
writer.align();
|
||||
if (obj)
|
||||
fnc(const_cast<T&>(*obj));
|
||||
fnc(ser, const_cast<T&>(*obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::optional<T> &obj, Fnc &&fnc) const {
|
||||
void deserialize(Des &des, Reader &reader, std::optional<T> &obj, Fnc &&fnc) const {
|
||||
bool exists{};
|
||||
des.boolValue(exists);
|
||||
if (_alignBeforeData)
|
||||
des.align();
|
||||
reader.align();
|
||||
if (exists) {
|
||||
obj = ::bitsery::Access::create<T>();
|
||||
fnc(*obj);
|
||||
fnc(des, *obj);
|
||||
} else {
|
||||
obj = std::nullopt;
|
||||
}
|
||||
|
||||
@@ -38,18 +38,18 @@ namespace bitsery {
|
||||
constexpr explicit StdSet(size_t maxSize):_maxSize{maxSize} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
void serialize(Ser &ser, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(writer, size);
|
||||
|
||||
for (auto &v:obj)
|
||||
fnc(const_cast<TKey &>(v));
|
||||
fnc(ser, const_cast<TKey &>(v));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
void deserialize(Des &des, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
|
||||
size_t size{};
|
||||
@@ -59,7 +59,7 @@ namespace bitsery {
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
fnc(key);
|
||||
fnc(des, key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,52 +70,52 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<typename TDeleter>
|
||||
static void create(std::unique_ptr<TElement, TDeleter>& obj, PolymorphicAllocator& alloc,
|
||||
static void create(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
size_t typeId) {
|
||||
obj.reset(alloc.allocate<TElement>(typeId));
|
||||
obj.reset(alloc.newObject<TElement>(typeId));
|
||||
}
|
||||
|
||||
template<typename TDeleter>
|
||||
static void createPolymorphic(std::unique_ptr<TElement, TDeleter>& obj, PolymorphicAllocator& alloc,
|
||||
static void createPolymorphic(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
obj.reset(static_cast<TElement*>(handler->create(alloc)));
|
||||
}
|
||||
|
||||
template<typename TDel>
|
||||
static void destroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
static void destroy(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) {
|
||||
uniquePtrDestroy(obj, alloc, typeId, std::is_same<std::unique_ptr<TElement>, T>{});
|
||||
}
|
||||
|
||||
template<typename TDel>
|
||||
static void destroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc,
|
||||
static void destroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
uniquePtrDestroyPolymorphic(obj, alloc, handler, std::is_same<std::unique_ptr<TElement>, T>{});
|
||||
}
|
||||
|
||||
static void destroy(std::shared_ptr<TElement>& obj, PolymorphicAllocator&, size_t) {
|
||||
static void destroy(std::shared_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
static void destroyPolymorphic(std::shared_ptr<TElement>& obj, PolymorphicAllocator&,
|
||||
static void destroyPolymorphic(std::shared_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
static void destroy(std::weak_ptr<TElement>& obj, PolymorphicAllocator&, size_t) {
|
||||
static void destroy(std::weak_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
static void destroyPolymorphic(std::weak_ptr<TElement>& obj, PolymorphicAllocator&,
|
||||
static void destroyPolymorphic(std::weak_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createShared(
|
||||
std::shared_ptr<TElement>& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
std::shared_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) {
|
||||
// capture deleter parameters by value
|
||||
obj = std::shared_ptr<TElement>(alloc.allocate<TElement>(typeId),
|
||||
[&alloc, typeId](TElement* data) {
|
||||
alloc.deallocate(data, typeId);
|
||||
obj = std::shared_ptr<TElement>(alloc.newObject<TElement>(typeId),
|
||||
[alloc, typeId](TElement* data) {
|
||||
alloc.deleteObject(data, typeId);
|
||||
});
|
||||
auto state = new SharedPtrSharedState{};
|
||||
state->obj = obj;
|
||||
@@ -123,7 +123,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedPolymorphic(
|
||||
std::shared_ptr<TElement>& obj, PolymorphicAllocator& alloc,
|
||||
std::shared_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
// capture deleter parameters by value
|
||||
obj = std::shared_ptr<TElement>(static_cast<TElement*>(handler->create(alloc)),
|
||||
@@ -136,10 +136,10 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createShared(
|
||||
std::weak_ptr<TElement>& obj, PolymorphicAllocator& alloc, size_t typeId) {
|
||||
auto res = std::shared_ptr<TElement>(alloc.allocate<TElement>(typeId),
|
||||
std::weak_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) {
|
||||
auto res = std::shared_ptr<TElement>(alloc.newObject<TElement>(typeId),
|
||||
[alloc, typeId](TElement* data) {
|
||||
alloc.deallocate(data, typeId);
|
||||
alloc.deleteObject(data, typeId);
|
||||
});
|
||||
obj = res;
|
||||
auto state = new SharedPtrSharedState{};
|
||||
@@ -148,7 +148,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
static std::unique_ptr<pointer_utils::PointerSharedStateBase> createSharedPolymorphic(
|
||||
std::weak_ptr<TElement>& obj, PolymorphicAllocator& alloc,
|
||||
std::weak_ptr<TElement>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
auto res = std::shared_ptr<TElement>(static_cast<TElement*>(handler->create(alloc)),
|
||||
[alloc, handler](TElement* data) {
|
||||
@@ -177,15 +177,15 @@ namespace bitsery {
|
||||
private:
|
||||
template<typename TDel>
|
||||
static void
|
||||
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc, size_t typeId,
|
||||
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId,
|
||||
std::true_type) {
|
||||
auto ptr = obj.release();
|
||||
alloc.deallocate(ptr, typeId);
|
||||
alloc.deleteObject(ptr, typeId);
|
||||
}
|
||||
|
||||
template<typename TDel>
|
||||
static void
|
||||
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator& alloc,
|
||||
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler, std::true_type) {
|
||||
auto ptr = obj.release();
|
||||
handler->destroy(alloc, ptr);
|
||||
@@ -193,14 +193,14 @@ namespace bitsery {
|
||||
|
||||
template<typename TDel>
|
||||
static void
|
||||
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator&, size_t,
|
||||
uniquePtrDestroy(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t,
|
||||
std::false_type) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
template<typename TDel>
|
||||
static void
|
||||
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, PolymorphicAllocator&,
|
||||
uniquePtrDestroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolymorphicAllocatorWithTypeId&,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&, std::false_type) {
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
@@ -37,59 +37,132 @@ namespace bitsery {
|
||||
public:
|
||||
virtual void* allocate(size_t bytes, size_t alignment, size_t typeId) = 0;
|
||||
|
||||
virtual void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) = 0;
|
||||
virtual void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) noexcept = 0;
|
||||
|
||||
virtual ~MemResourceBase() noexcept = default;
|
||||
};
|
||||
|
||||
// default implementation for MemResourceBase using new and delete
|
||||
class MemResourceNewDelete : public MemResourceBase {
|
||||
public:
|
||||
inline void* allocate(size_t bytes, size_t /*alignment*/, size_t /*typeId*/) final {
|
||||
return (::operator new(bytes));
|
||||
}
|
||||
|
||||
inline void deallocate(void* ptr, size_t /*bytes*/, size_t /*alignment*/, size_t /*typeId*/) final {
|
||||
inline void
|
||||
deallocate(void* ptr, size_t /*bytes*/, size_t /*alignment*/, size_t /*typeId*/) noexcept final {
|
||||
(::operator delete(ptr));
|
||||
}
|
||||
|
||||
~MemResourceNewDelete() noexcept final = default;
|
||||
};
|
||||
|
||||
class PolymorphicAllocator {
|
||||
public:
|
||||
// these classes are used internally by bitsery extensions and and pointer utils
|
||||
namespace pointer_utils {
|
||||
// this is helper class that stores memory resource and knows how to construct/destroy objects
|
||||
// capture this by value for custom deleters, because during deserialization mem resource can be changed
|
||||
class PolymorphicAllocatorWithTypeId final {
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
T* allocate(size_t typeId) const {
|
||||
constexpr auto bytes = sizeof(T);
|
||||
constexpr auto alignment = std::alignment_of<T>::value;
|
||||
void* ptr = _resource
|
||||
? _resource->allocate(bytes, alignment, typeId)
|
||||
: MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
|
||||
return ::bitsery::Access::create<T>(ptr);
|
||||
}
|
||||
explicit constexpr PolymorphicAllocatorWithTypeId(MemResourceBase* memResource = nullptr)
|
||||
:_resource{memResource} {}
|
||||
|
||||
template<typename T>
|
||||
void deallocate(T* ptr, size_t typeId) const {
|
||||
constexpr auto bytes = sizeof(T);
|
||||
constexpr auto alignment = std::alignment_of<T>::value;
|
||||
ptr->~T();
|
||||
_resource
|
||||
? _resource->deallocate(ptr, bytes, alignment, typeId)
|
||||
: MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
|
||||
}
|
||||
template<typename T>
|
||||
T* allocate(size_t n, size_t typeId) const {
|
||||
const auto bytes = sizeof(T) * n;
|
||||
constexpr auto alignment = std::alignment_of<T>::value;
|
||||
void* ptr = _resource
|
||||
? _resource->allocate(bytes, alignment, typeId)
|
||||
: ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
void setMemResource(MemResourceBase* resource) {
|
||||
_resource = resource;
|
||||
}
|
||||
template<typename T>
|
||||
void deallocate(T* ptr, size_t n, size_t typeId) const noexcept {
|
||||
const auto bytes = sizeof(T) * n;
|
||||
constexpr auto alignment = std::alignment_of<T>::value;
|
||||
_resource
|
||||
? _resource->deallocate(ptr, bytes, alignment, typeId)
|
||||
: ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
|
||||
}
|
||||
|
||||
MemResourceBase* getMemResource() const {
|
||||
return _resource;
|
||||
}
|
||||
template<typename T>
|
||||
T* newObject(size_t typeId) const {
|
||||
auto ptr = allocate<T>(1, typeId);
|
||||
return ::bitsery::Access::create<T>(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
MemResourceBase* _resource{nullptr};
|
||||
};
|
||||
template<typename T>
|
||||
void deleteObject(T* obj, size_t typeId) const {
|
||||
obj->~T();
|
||||
deallocate(obj, 1, typeId);
|
||||
}
|
||||
|
||||
void setMemResource(ext::MemResourceBase* resource) {
|
||||
_resource = resource;
|
||||
}
|
||||
|
||||
ext::MemResourceBase* getMemResource() const {
|
||||
return _resource;
|
||||
}
|
||||
|
||||
bool operator==(const PolymorphicAllocatorWithTypeId& rhs) const noexcept {
|
||||
return _resource == rhs._resource;
|
||||
}
|
||||
|
||||
bool operator!=(const PolymorphicAllocatorWithTypeId& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ext::MemResourceBase* _resource;
|
||||
};
|
||||
|
||||
// this is very similar to c++17 PolymorphicAllocator, it is in extensions, that need to allocate memory
|
||||
// it just wraps our PolymorphicAllocatorWithTypeId and pass 0 as typeId
|
||||
// and defines core functions for c++ Allocator concept,
|
||||
template<class T>
|
||||
struct PolymorphicAllocatorWrapper final {
|
||||
using value_type = T;
|
||||
|
||||
explicit constexpr PolymorphicAllocatorWrapper(MemResourceBase* memResource)
|
||||
:_alloc{memResource} {}
|
||||
explicit constexpr PolymorphicAllocatorWrapper(PolymorphicAllocatorWithTypeId alloc) : _alloc{alloc} {}
|
||||
|
||||
template <typename U>
|
||||
friend class PolymorphicAllocatorWrapper;
|
||||
|
||||
template<class U>
|
||||
constexpr explicit PolymorphicAllocatorWrapper(const PolymorphicAllocatorWrapper<U>& other) noexcept
|
||||
:_alloc{other._alloc} {
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n) {
|
||||
return _alloc.allocate<T>(n, 0);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n) noexcept {
|
||||
return _alloc.deallocate(p, n, 0);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend bool operator==(const PolymorphicAllocatorWrapper<T>& lhs,
|
||||
const PolymorphicAllocatorWrapper<U>& rhs) noexcept {
|
||||
return lhs._alloc == rhs._alloc;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend bool operator!=(const PolymorphicAllocatorWrapper<T>& lhs,
|
||||
const PolymorphicAllocatorWrapper<U>& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
PolymorphicAllocatorWithTypeId _alloc;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif //BITSERY_EXT_MEMORY_ALLOCATOR_H
|
||||
|
||||
@@ -213,7 +213,12 @@ namespace bitsery {
|
||||
});
|
||||
}
|
||||
|
||||
PolymorphicAllocatorWithTypeId& getAllocator() {
|
||||
return _polyAlloc;
|
||||
}
|
||||
|
||||
private:
|
||||
PolymorphicAllocatorWithTypeId _polyAlloc{};
|
||||
std::unordered_map<size_t, PLCInfoDeserializer> _idMap;
|
||||
};
|
||||
}
|
||||
@@ -221,8 +226,7 @@ namespace bitsery {
|
||||
//this class is for convenience
|
||||
class PointerLinkingContext :
|
||||
public pointer_utils::PointerLinkingContextSerialization,
|
||||
public pointer_utils::PointerLinkingContextDeserialization,
|
||||
public PolymorphicAllocator {
|
||||
public pointer_utils::PointerLinkingContextDeserialization {
|
||||
public:
|
||||
explicit PointerLinkingContext() = default;
|
||||
|
||||
@@ -261,9 +265,8 @@ namespace bitsery {
|
||||
|
||||
auto ptr = TPtrManager<T>::getPtr(const_cast<T&>(obj));
|
||||
if (ptr) {
|
||||
auto ctx = ser.template context<PointerLinkingContext>();
|
||||
assert(ctx != nullptr);
|
||||
auto& ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
|
||||
auto& ctx = ser.template context<PointerLinkingContext>();
|
||||
auto& ptrInfo = ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
|
||||
details::writeSize(w, ptrInfo.id);
|
||||
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
|
||||
if (!ptrInfo.isSharedProcessed)
|
||||
@@ -280,39 +283,39 @@ namespace bitsery {
|
||||
void deserialize(Des& des, Reader& r, T& obj, Fnc&& fnc) const {
|
||||
size_t id{};
|
||||
details::readSize(r, id, std::numeric_limits<size_t>::max());
|
||||
auto ctx = des.template context<PointerLinkingContext>();
|
||||
assert(ctx != nullptr);
|
||||
auto& ctx = des.template context<PointerLinkingContext>();
|
||||
auto& alloc = ctx.getAllocator();
|
||||
if (id) {
|
||||
auto& ptrInfo = ctx->getInfoById(id, TPtrManager<T>::getOwnership());
|
||||
deserializeImpl(*ctx, ptrInfo, des, obj, std::forward<Fnc>(fnc), r, IsPolymorphic<T>{},
|
||||
auto& ptrInfo = ctx.getInfoById(id, TPtrManager<T>::getOwnership());
|
||||
deserializeImpl(alloc, ptrInfo, des, obj, std::forward<Fnc>(fnc), r, IsPolymorphic<T>{},
|
||||
OwnershipType<TPtrManager<T>::getOwnership()>{});
|
||||
} else {
|
||||
if (_ptrType == PointerType::Nullable) {
|
||||
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
|
||||
auto prevMemResource = ctx->getMemResource();
|
||||
if (_resource) ctx->setMemResource(_resource);
|
||||
destroyPtr(*ctx, des, obj, IsPolymorphic<T>{});
|
||||
ctx->setMemResource(prevMemResource);
|
||||
auto prevMemResource = alloc.getMemResource();
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
destroyPtr(alloc, des, obj, IsPolymorphic<T>{});
|
||||
alloc.setMemResource(prevMemResource);
|
||||
};
|
||||
} else
|
||||
r.setError(ReaderError::InvalidPointer);
|
||||
r.error(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename Des, typename TObj>
|
||||
void destroyPtr(PointerLinkingContext& plc, Des& des, TObj& obj,
|
||||
void destroyPtr(PolymorphicAllocatorWithTypeId& alloc, Des& des, TObj& obj,
|
||||
std::true_type /*polymorphic*/) const {
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
auto ptr = TPtrManager<TObj>::getPtr(obj);
|
||||
TPtrManager<TObj>::destroyPolymorphic(obj, plc, ctx->getPolymorphicHandler(*ptr));
|
||||
TPtrManager<TObj>::destroyPolymorphic(obj, alloc, ctx.getPolymorphicHandler(*ptr));
|
||||
}
|
||||
|
||||
template<typename Des, typename TObj>
|
||||
void destroyPtr(PointerLinkingContext& plc, Des&, TObj& obj,
|
||||
void destroyPtr(PolymorphicAllocatorWithTypeId& alloc, Des&, TObj& obj,
|
||||
std::false_type /*polymorphic*/) const {
|
||||
TPtrManager<TObj>::destroy(obj, plc, RTTI::template get<typename TPtrManager<TObj>::TElement>());
|
||||
TPtrManager<TObj>::destroy(obj, alloc, RTTI::template get<typename TPtrManager<TObj>::TElement>());
|
||||
}
|
||||
|
||||
|
||||
@@ -327,74 +330,74 @@ namespace bitsery {
|
||||
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
|
||||
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, Writer& w, std::true_type) const {
|
||||
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
|
||||
ctx->serialize(ser, w, *ptr);
|
||||
ctx.serialize(ser, w, *ptr);
|
||||
}
|
||||
|
||||
template<typename Ser, typename TPtr, typename Fnc, typename Writer>
|
||||
void serializeImpl(Ser&, TPtr& ptr, Fnc&& fnc, Writer&, std::false_type) const {
|
||||
fnc(*ptr);
|
||||
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&& fnc, Writer&, std::false_type) const {
|
||||
fnc(ser, *ptr);
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId& alloc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
|
||||
Reader& r, std::true_type, OwnershipType<PointerOwnershipType::Owner>) const {
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
auto prevMemResource = plc.getMemResource();
|
||||
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, &plc, this, prevMemResource](
|
||||
auto prevMemResource = alloc.getMemResource();
|
||||
ctx.deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, &alloc, this, prevMemResource](
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
TPtrManager<T>::createPolymorphic(obj, plc, handler);
|
||||
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
TPtrManager<T>::createPolymorphic(obj, alloc, handler);
|
||||
if (!_resourcePropagate) alloc.setMemResource(prevMemResource);
|
||||
return TPtrManager<T>::getPtr(obj);
|
||||
},
|
||||
[&obj, &plc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
TPtrManager<T>::destroyPolymorphic(obj, plc, handler);
|
||||
[&obj, &alloc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
TPtrManager<T>::destroyPolymorphic(obj, alloc, handler);
|
||||
});
|
||||
plc.setMemResource(prevMemResource);
|
||||
alloc.setMemResource(prevMemResource);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&& fnc,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId& alloc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
|
||||
Reader&, std::false_type, OwnershipType<PointerOwnershipType::Owner>) const {
|
||||
auto ptr = TPtrManager<T>::getPtr(obj);
|
||||
if (ptr) {
|
||||
fnc(*ptr);
|
||||
fnc(des, *ptr);
|
||||
} else {
|
||||
auto prevMemResource = plc.getMemResource();
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
TPtrManager<T>::create(obj, plc, RTTI::template get<typename TPtrManager<T>::TElement>());
|
||||
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
|
||||
auto prevMemResource = alloc.getMemResource();
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
TPtrManager<T>::create(obj, alloc, RTTI::template get<typename TPtrManager<T>::TElement>());
|
||||
if (!_resourcePropagate) alloc.setMemResource(prevMemResource);
|
||||
ptr = TPtrManager<T>::getPtr(obj);
|
||||
fnc(*ptr);
|
||||
plc.setMemResource(prevMemResource);
|
||||
fnc(des, *ptr);
|
||||
alloc.setMemResource(prevMemResource);
|
||||
}
|
||||
ptrInfo.processOwner(ptr);
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId& alloc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
|
||||
Reader& r, std::true_type,
|
||||
OwnershipType<PointerOwnershipType::SharedOwner>) const {
|
||||
auto& sharedState = ptrInfo.sharedState;
|
||||
if (!sharedState) {
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
auto prevMemResource = plc.getMemResource();
|
||||
ctx->deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, &plc, &sharedState, this, prevMemResource](
|
||||
auto prevMemResource = alloc.getMemResource();
|
||||
ctx.deserialize(des, r, TPtrManager<T>::getPtr(obj),
|
||||
[&obj, &alloc, &sharedState, this, prevMemResource](
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
sharedState = TPtrManager<T>::createSharedPolymorphic(obj, plc, handler);
|
||||
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
sharedState = TPtrManager<T>::createSharedPolymorphic(obj, alloc, handler);
|
||||
if (!_resourcePropagate) alloc.setMemResource(prevMemResource);
|
||||
return TPtrManager<T>::getPtr(obj);
|
||||
},
|
||||
[&obj, &plc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
TPtrManager<T>::destroyPolymorphic(obj, plc, handler);
|
||||
[&obj, &alloc, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
TPtrManager<T>::destroyPolymorphic(obj, alloc, handler);
|
||||
});
|
||||
plc.setMemResource(prevMemResource);
|
||||
alloc.setMemResource(prevMemResource);
|
||||
if (!sharedState)
|
||||
sharedState = TPtrManager<T>::getSharedState(obj);
|
||||
}
|
||||
@@ -403,38 +406,38 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader>
|
||||
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&& fnc,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId& alloc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
|
||||
Reader&, std::false_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
|
||||
auto& sharedState = ptrInfo.sharedState;
|
||||
if (!sharedState) {
|
||||
auto ptr = TPtrManager<T>::getPtr(obj);
|
||||
auto prevMemResource = plc.getMemResource();
|
||||
auto prevMemResource = alloc.getMemResource();
|
||||
if (ptr) {
|
||||
sharedState = TPtrManager<T>::getSharedState(obj);
|
||||
} else {
|
||||
if (_resource) plc.setMemResource(_resource);
|
||||
sharedState = TPtrManager<T>::createShared(obj, plc,
|
||||
if (_resource) alloc.setMemResource(_resource);
|
||||
sharedState = TPtrManager<T>::createShared(obj, alloc,
|
||||
RTTI::template get<typename TPtrManager<T>::TElement>());
|
||||
if (!_resourcePropagate) plc.setMemResource(prevMemResource);
|
||||
if (!_resourcePropagate) alloc.setMemResource(prevMemResource);
|
||||
ptr = TPtrManager<T>::getPtr(obj);
|
||||
}
|
||||
fnc(*ptr);
|
||||
plc.setMemResource(prevMemResource);
|
||||
fnc(des, *ptr);
|
||||
alloc.setMemResource(prevMemResource);
|
||||
}
|
||||
TPtrManager<T>::loadFromSharedState(sharedState.get(), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorph>
|
||||
void deserializeImpl(PointerLinkingContext& plc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId& alloc, PLCInfoDeserializer& ptrInfo, Des& des, T& obj,
|
||||
Fnc&& fnc, Reader& r, isPolymorph polymorph,
|
||||
OwnershipType<PointerOwnershipType::SharedObserver>) const {
|
||||
deserializeImpl(plc, ptrInfo, des, obj, fnc, r, polymorph,
|
||||
deserializeImpl(alloc, ptrInfo, des, obj, fnc, r, polymorph,
|
||||
OwnershipType<PointerOwnershipType::SharedOwner>{});
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename Reader, typename isPolymorphic>
|
||||
void deserializeImpl(PointerLinkingContext&, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&,
|
||||
void deserializeImpl(PolymorphicAllocatorWithTypeId&, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&,
|
||||
Reader&, isPolymorphic, OwnershipType<PointerOwnershipType::Observer>) const {
|
||||
ptrInfo.processObserver(reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ namespace bitsery {
|
||||
|
||||
class PolymorphicHandlerBase {
|
||||
public:
|
||||
virtual void* create(const PolymorphicAllocator& alloc) const = 0;
|
||||
virtual void* create(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc) const = 0;
|
||||
|
||||
virtual void destroy(const PolymorphicAllocator& alloc, void* ptr) const = 0;
|
||||
virtual void destroy(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc, void* ptr) const = 0;
|
||||
|
||||
virtual void process(void* ser, void* obj) const = 0;
|
||||
|
||||
@@ -74,12 +74,12 @@ namespace bitsery {
|
||||
class PolymorphicHandler : public PolymorphicHandlerBase {
|
||||
public:
|
||||
|
||||
void* create(const PolymorphicAllocator& alloc) const final {
|
||||
return toBase(alloc.allocate<TDerived>(RTTI::template get<TDerived>()));
|
||||
void* create(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc) const final {
|
||||
return toBase(alloc.newObject<TDerived>(RTTI::template get<TDerived>()));
|
||||
}
|
||||
|
||||
void destroy(const PolymorphicAllocator& alloc, void* ptr) const final {
|
||||
alloc.deallocate<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
|
||||
void destroy(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc, void* ptr) const final {
|
||||
alloc.deleteObject<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
|
||||
}
|
||||
|
||||
void process(void* ser, void* obj) const final {
|
||||
@@ -165,17 +165,6 @@ namespace bitsery {
|
||||
_baseToDerivedArray.clear();
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
|
||||
[[deprecated("de/serializer instance is not required")]] void
|
||||
registerBasesList(const TSerializer& s, PolymorphicClassesList<T1, Tn...>) {
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
registerBasesList<TSerializer, THierarchy>(s, PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
template<typename TSerializer, template<typename> class THierarchy>
|
||||
[[deprecated]] void registerBasesList(const TSerializer&, PolymorphicClassesList<>) {
|
||||
}
|
||||
|
||||
// THierarchy is the name of class, that defines hierarchy
|
||||
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
|
||||
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
|
||||
@@ -200,7 +189,7 @@ namespace bitsery {
|
||||
|
||||
|
||||
template<typename Serializer, typename Writer, typename TBase>
|
||||
void serialize(Serializer& ser, Writer& writer, TBase& obj) {
|
||||
void serialize(Serializer& ser, Writer& writer, TBase& obj) const {
|
||||
//get derived key
|
||||
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)};
|
||||
auto it = _baseToDerivedMap.find(key);
|
||||
@@ -218,7 +207,7 @@ namespace bitsery {
|
||||
|
||||
template<typename Deserializer, typename Reader, typename TBase, typename TCreateFnc, typename TDestroyFnc>
|
||||
void deserialize(Deserializer& des, Reader& reader, TBase* obj,
|
||||
TCreateFnc createFnc, TDestroyFnc destroyFnc) {
|
||||
TCreateFnc createFnc, TDestroyFnc destroyFnc) const {
|
||||
size_t derivedIndex{};
|
||||
details::readSize(reader, derivedIndex, std::numeric_limits<size_t>::max());
|
||||
|
||||
@@ -240,7 +229,7 @@ namespace bitsery {
|
||||
}
|
||||
handler->process(&des, obj);
|
||||
} else
|
||||
reader.setError(ReaderError::InvalidPointer);
|
||||
reader.error(ReaderError::InvalidPointer);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace bitsery {
|
||||
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.setError(ReaderError::InvalidData);
|
||||
reader.error(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace bitsery {
|
||||
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::map<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](Key& key, T& value) {
|
||||
[](S& s, Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
@@ -40,7 +40,7 @@ namespace bitsery {
|
||||
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
|
||||
void serialize(S &s, std::multimap<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](Key& key, T& value) {
|
||||
[](S& s, Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace bitsery {
|
||||
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_map<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](Key& key, T& value) {
|
||||
[](S& s, Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
@@ -40,7 +40,7 @@ namespace bitsery {
|
||||
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
void serialize(S &s, std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](Key& key, T& value) {
|
||||
[](S& s, Key& key, T& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
|
||||
@@ -30,50 +30,32 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename TAdapterWriter, typename TContext = void>
|
||||
template<typename TAdapterWriter>
|
||||
class BasicSerializer {
|
||||
public:
|
||||
//this is used by AdapterAccess class
|
||||
using TWriter = TAdapterWriter;
|
||||
//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, TContext>;
|
||||
TAdapterWriter,
|
||||
AdapterWriterBitPackingWrapper<TAdapterWriter>>::type>;
|
||||
|
||||
static_assert(details::IsSpecializationOf<typename TWriter::TConfig::InternalContext, std::tuple>::value,
|
||||
"Config::InternalContext must be std::tuple");
|
||||
|
||||
template <typename WriterParam>
|
||||
explicit BasicSerializer(WriterParam&& w, TContext* context = nullptr)
|
||||
: _writer{std::forward<WriterParam>(w)},
|
||||
_context{context},
|
||||
_internalContext{}
|
||||
explicit BasicSerializer(TAdapterWriter& writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
//copying disabled
|
||||
BasicSerializer(const BasicSerializer&) = delete;
|
||||
BasicSerializer& operator = (const BasicSerializer&) = delete;
|
||||
|
||||
//move enabled
|
||||
BasicSerializer(BasicSerializer&& ) = default;
|
||||
BasicSerializer& operator = (BasicSerializer&& ) = default;
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
TContext* context() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* context() {
|
||||
return details::getContext<T>(_context, _internalContext);
|
||||
T& context() {
|
||||
return *details::getContext<true, T>(_writer.context());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* contextOrNull() {
|
||||
return details::getContextIfTypeExists<T>(_context, _internalContext);
|
||||
return details::getContext<false, T>(_writer.context());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -86,7 +68,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void object(const T &obj, Fnc &&fnc) {
|
||||
fnc(const_cast<T& >(obj));
|
||||
fnc(*this, const_cast<T& >(obj));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,7 +126,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, [this](VType &v) { value<VSIZE>(v); });
|
||||
extension.serialize(*this, _writer, obj, [](BasicSerializer& s, VType &v) { s.value<VSIZE>(v); });
|
||||
}
|
||||
|
||||
template<typename T, typename Ext>
|
||||
@@ -154,7 +136,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, [this](VType &v) { object(v); });
|
||||
extension.serialize(*this, _writer, obj, [](BasicSerializer& s, VType &v) { s.object(v); });
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -261,10 +243,6 @@ namespace bitsery {
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
void align() {
|
||||
_writer.align();
|
||||
}
|
||||
|
||||
//overloads for functions with explicit type size
|
||||
|
||||
template<typename T>
|
||||
@@ -333,15 +311,10 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
|
||||
private:
|
||||
friend AdapterAccess;
|
||||
// this is required when creating bitpacking serializer, to access internal context
|
||||
friend class BasicSerializer<typename details::GetNonWrappedAdapterWriter<TAdapterWriter>::Writer, TContext>;
|
||||
|
||||
TAdapterWriter _writer;
|
||||
TContext* _context;
|
||||
typename TWriter::TConfig::InternalContext _internalContext;
|
||||
|
||||
TAdapterWriter& _writer;
|
||||
|
||||
//process value types
|
||||
//false_type means that we must process all elements individually
|
||||
@@ -367,7 +340,7 @@ namespace bitsery {
|
||||
void procContainer(It first, It last, Fnc fnc) {
|
||||
using TValue = typename std::decay<decltype(*first)>::type;
|
||||
for (; first != last; ++first) {
|
||||
fnc(const_cast<TValue&>(*first));
|
||||
fnc(*this, const_cast<TValue&>(*first));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,11 +379,9 @@ namespace bitsery {
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
BPEnabledType tmp(_writer, _context);
|
||||
// move internal context to and from of bitpacking enabled serializer
|
||||
tmp._internalContext = std::move(_internalContext);
|
||||
fnc(tmp);
|
||||
_internalContext = std::move(tmp._internalContext);
|
||||
AdapterWriterBitPackingWrapper<TAdapterWriter> bitPackingWrapper{_writer};
|
||||
BPEnabledType serializer{bitPackingWrapper};
|
||||
fnc(serializer);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
@@ -429,28 +400,23 @@ namespace bitsery {
|
||||
|
||||
};
|
||||
|
||||
|
||||
//helper type
|
||||
template <typename Adapter>
|
||||
using Serializer = BasicSerializer<AdapterWriter<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
|
||||
template <typename Adapter, typename T>
|
||||
size_t quickSerialization(Adapter adapter, const T& value) {
|
||||
Serializer<Adapter> ser{std::move(adapter)};
|
||||
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};
|
||||
ser.object(value);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
writer.flush();
|
||||
return writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t quickMeasureSize(const T& value) {
|
||||
BasicSerializer<MeasureSize> ser{MeasureSize{}};
|
||||
MeasureSize writer{};
|
||||
BasicSerializer<MeasureSize> ser{writer};
|
||||
ser.object(value);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
writer.flush();
|
||||
return writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -147,7 +147,6 @@ namespace bitsery {
|
||||
//it is called only current buffer size is not enough to write.
|
||||
//it is used to dramaticaly improve performance by updating buffer directly
|
||||
//instead of using back_insert_iterator to append each byte to buffer.
|
||||
//thats why Writer return range iterators
|
||||
|
||||
static void increaseBufferSize(T& ) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
|
||||
512
tests/adapter.cpp
Normal file
512
tests/adapter.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
//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 <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.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;
|
||||
|
||||
TEST(OutputBuffer, WhenInitialBufferIsEmptyThenResizeInAdapterConstructor) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
EXPECT_THAT(buf.size(), Eq(0));
|
||||
OutputAdapter adapter{buf};
|
||||
EXPECT_THAT(buf.size(), Ge(1));
|
||||
}
|
||||
|
||||
TEST(OutputBuffer, WhenSetWritePositionThenResizeUnderlyingBufferIfRequired) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
const auto initialSize = buf.size();
|
||||
EXPECT_THAT(buf.size(), Eq(initialSize));
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(0));
|
||||
w.currentWritePos(initialSize + 10);
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(initialSize + 10));
|
||||
EXPECT_THAT(buf.size(), Ge(initialSize + 10));
|
||||
}
|
||||
|
||||
TEST(OutputBuffer, WhenSettingCurrentPositionBeforeBufferEndThenWrittenBytesCountIsNotAffected) {
|
||||
//setup data
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
const auto initialSize = buf.size();
|
||||
EXPECT_THAT(buf.size(), Eq(initialSize));
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(0));
|
||||
w.currentWritePos(initialSize + 10);
|
||||
w.writeBytes<8>(static_cast<uint64_t>(1));
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8));
|
||||
w.currentWritePos(0);
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(initialSize + 10 + 8));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, CorrectlySetsAndGetsCurrentReadPosition) {
|
||||
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
r.currentReadPos(5);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(5));
|
||||
r.currentReadPos(0);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(0));
|
||||
uint8_t tmp;
|
||||
r.readBytes<1>(tmp);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(1));
|
||||
}
|
||||
|
||||
|
||||
TEST(InputBuffer, WhenSetReadPositionOutOfRangeThenDataOverflow) {
|
||||
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
r.currentReadPos(10);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
r.currentReadPos(11);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, WhenSetReadEndPositionOutOfRangeThenDataOverflow) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
r.currentReadEndPos(11);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, WhenReadEndPositionIsNotSetThenReturnZeroAsBufferEndPosition) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(0));
|
||||
r.currentReadEndPos(5);
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(5));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
}
|
||||
|
||||
|
||||
TEST(InputBuffer, WhenReadEndPositionIsNotZeroThenDataOverflowErrorWillBeIgnored) {
|
||||
Buffer buf{};
|
||||
buf.resize(100);
|
||||
Reader r{{buf.begin(), 1}};
|
||||
r.currentReadEndPos(1);
|
||||
uint32_t tmp{};
|
||||
r.readBytes<4>(tmp);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
r.currentReadEndPos(0);
|
||||
r.readBytes<4>(tmp);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
|
||||
TEST(InputBuffer, WhenReadingPastReadEndPositionOrBufferEndThenReadPositionDoesntChange) {
|
||||
Buffer buf{};
|
||||
buf.resize(10);
|
||||
Reader r{{buf.begin(), 3}};
|
||||
uint32_t tmp{};
|
||||
r.currentReadEndPos(2);
|
||||
r.readBytes<4>(tmp);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(0));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
r.currentReadEndPos(0);
|
||||
r.readBytes<4>(tmp);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(0));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, WhenReaderHasErrorsThenSettingReadPosAndReadEndPosIsIgnoredAndGettingAlwaysReturnsZero) {
|
||||
Buffer buf{};
|
||||
buf.resize(10);
|
||||
Reader r{{buf.begin(), 10}};
|
||||
uint32_t tmp{};
|
||||
r.readBytes<4>(tmp);
|
||||
r.currentReadEndPos(5);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(4));
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(5));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
r.currentReadEndPos(11);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(0));
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(0));
|
||||
r.currentReadPos(1);
|
||||
r.currentReadEndPos(1);
|
||||
EXPECT_THAT(r.currentReadPos(), Eq(0));
|
||||
EXPECT_THAT(r.currentReadEndPos(), Eq(0));
|
||||
}
|
||||
|
||||
TEST(InputBuffer, ConstDataForBufferAllAdapters) {
|
||||
//create and write to buffer
|
||||
uint16_t data = 7549;
|
||||
Buffer bufWrite{};
|
||||
Writer 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()}};
|
||||
|
||||
uint16_t res1{};
|
||||
r1.readBytes<2>(res1);
|
||||
|
||||
uint16_t res2{};
|
||||
r2.readBytes<2>(res2);
|
||||
EXPECT_THAT(res1, Eq(data));
|
||||
EXPECT_THAT(res2, Eq(data));
|
||||
}
|
||||
|
||||
|
||||
template <template<typename...> class TAdapter>
|
||||
struct BufferConfig {
|
||||
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) {
|
||||
data = buffer;
|
||||
return Reader{Adapter{data.begin(), data.size()}};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TAdapter>
|
||||
struct StreamConfig {
|
||||
using Data = std::stringstream;
|
||||
using Adapter = TAdapter;
|
||||
using Reader = bitsery::AdapterReader<Adapter, bitsery::DefaultConfig>;
|
||||
|
||||
Data data{};
|
||||
Reader createReader(const std::vector<char>& buffer) {
|
||||
std::string str(buffer.begin(), buffer.end());
|
||||
data = std::stringstream{str};
|
||||
return Reader{Adapter{data}};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TAdapterWithData>
|
||||
class AdapterConfig : public testing::Test {
|
||||
public:
|
||||
|
||||
TAdapterWithData config{};
|
||||
};
|
||||
|
||||
using AdapterInputTypes = ::testing::Types<
|
||||
BufferConfig<bitsery::InputBufferAdapter>,
|
||||
BufferConfig<bitsery::UnsafeInputBufferAdapter>,
|
||||
StreamConfig<bitsery::InputStreamAdapter>
|
||||
>;
|
||||
|
||||
template <typename TConfig>
|
||||
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));
|
||||
r.error(ReaderError::InvalidPointer);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer));
|
||||
r.error(ReaderError::DataOverflow);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer));
|
||||
r.error(ReaderError::NoError);
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::InvalidPointer));
|
||||
}
|
||||
|
||||
|
||||
TYPED_TEST(InputAll, WhenAlignHasNonZerosThenInvalidDataError) {
|
||||
|
||||
auto r = this->config.createReader({0x7F});
|
||||
bitsery::AdapterReaderBitPackingWrapper<decltype(r)> bpr{r};
|
||||
|
||||
uint8_t tmp{0xFF};
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.error(), Eq(ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
TYPED_TEST(InputAll, 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));
|
||||
|
||||
EXPECT_THAT(rb, Eq(tb));
|
||||
EXPECT_THAT(rc, Eq(tc));
|
||||
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) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
buf.resize(w.writtenBytesCount());
|
||||
|
||||
auto r = this->config.createReader(buf);
|
||||
|
||||
uint8_t r1{};
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
r.template readBytes<1>(r1);
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::NoError));
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
r.template readBytes<1>(r1);
|
||||
r.template readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
EXPECT_THAT(r.error(), Eq(ReaderError::DataOverflow));
|
||||
|
||||
}
|
||||
|
||||
TYPED_TEST(InputSafeOnly, WhenReaderHasErrorsAllThenReadsReturnZero) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Buffer buf{};
|
||||
Writer w{buf};
|
||||
w.writeBytes<1>(t1);
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
buf.resize(w.writtenBytesCount());
|
||||
|
||||
auto r = this->config.createReader(buf);
|
||||
|
||||
uint8_t r1{};
|
||||
r.template readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
r.error(ReaderError::InvalidPointer);
|
||||
r.template readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
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>;
|
||||
|
||||
static constexpr size_t InternalBufferSize = 128;
|
||||
|
||||
std::stringstream stream{};
|
||||
|
||||
Writer writer{{stream, 128}};
|
||||
};
|
||||
|
||||
using BufferedAdapterInternalBufferTypes = ::testing::Types<
|
||||
std::vector<char>,
|
||||
std::array<char, 128>,
|
||||
std::string
|
||||
>;
|
||||
|
||||
TYPED_TEST_CASE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes);
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenBufferOverflowThenWriteBufferAndRemainingDataToStream) {
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_TRUE(this->stream.str().empty());
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
|
||||
}
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) {
|
||||
uint8_t x{};
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(0));
|
||||
this->writer.flush();
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(1));
|
||||
this->writer.flush();
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect) {
|
||||
|
||||
//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}};
|
||||
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
w.template writeBytes<1>(x);
|
||||
static constexpr bool ShouldWriteToStream = bitsery::traits::ContainerTraits<typename TestFixture::Buffer>::isResizable;
|
||||
EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream));
|
||||
}
|
||||
|
||||
TEST(AdapterWriterMeasureSize, CorrectlyMeasuresWrittenBytesCountForSerialization) {
|
||||
bitsery::MeasureSize w{};
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(0));
|
||||
w.writeBytes<8>(uint64_t{0});
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(8));
|
||||
w.writeBuffer<8, uint64_t>(nullptr, 9);
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(80));
|
||||
w.currentWritePos(10);
|
||||
w.writeBytes<4>(uint32_t{0});
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(80));
|
||||
EXPECT_THAT(w.currentWritePos(), Eq(14));
|
||||
w.currentWritePos(80);
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(80));
|
||||
w.writeBits(uint32_t{0}, 7u);
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(80));
|
||||
w.align();
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(81));
|
||||
w.writeBits(uint32_t{0}, 7u);
|
||||
w.flush();
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(82));
|
||||
// doesn't compile on older compilers if I write bitsery::MeasureSize::BitPackingEnabled directly in EXPECT_THAT macro.
|
||||
constexpr bool bpEnabled = bitsery::MeasureSize::BitPackingEnabled;
|
||||
EXPECT_THAT(bpEnabled, Eq(true));
|
||||
}
|
||||
|
||||
|
||||
struct 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();
|
||||
}
|
||||
@@ -1,162 +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.
|
||||
|
||||
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <sstream>
|
||||
|
||||
//some helper types
|
||||
using Stream = std::stringstream;
|
||||
using OutputAdapter = bitsery::OutputStreamAdapter;
|
||||
using InputAdapter = bitsery::InputStreamAdapter ;
|
||||
using Writer = bitsery::AdapterWriter<bitsery::OutputStreamAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<bitsery::InputStreamAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
static constexpr size_t InternalBufferSize = 128;
|
||||
using BufferedAdapterInternalBuffer = std::array<char, InternalBufferSize>;
|
||||
using OutputBufferedAdapter = bitsery::BasicBufferedOutputStreamAdapter<char, std::char_traits<char>, BufferedAdapterInternalBuffer>;
|
||||
using BufferedWriter = bitsery::AdapterWriter<OutputBufferedAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
TEST(AdapterIOStream, CorrectlyReturnsIsCompletedSuccessfully) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
}
|
||||
|
||||
TEST(AdapterIOStream, ReadingMoreThanAvailableReturnsZero) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
r.readBytes<1>(r1);
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
}
|
||||
|
||||
//this is strange, but probably stringstream doesnt use any of the base methods that sets io_base::iostate flags
|
||||
TEST(AdapterIOStream, WhenReadingMoreThanAvailableThenDataOverflow) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::NoError));
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
r.readBytes<1>(r1);
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class AdapterBufferedOutputStream : public testing::Test {
|
||||
public:
|
||||
using Buffer = T;
|
||||
using Adapter = bitsery::BasicBufferedOutputStreamAdapter<char, std::char_traits<char>, Buffer>;
|
||||
using Writer = bitsery::AdapterWriter<Adapter, bitsery::DefaultConfig>;
|
||||
|
||||
static constexpr size_t InternalBufferSize = 128;
|
||||
|
||||
Stream stream{};
|
||||
|
||||
Writer writer{{stream, 128}};
|
||||
};
|
||||
|
||||
using BufferedAdapterInternalBufferTypes = ::testing::Types<
|
||||
std::vector<char>,
|
||||
std::array<char, 128>,
|
||||
std::string
|
||||
>;
|
||||
|
||||
TYPED_TEST_CASE(AdapterBufferedOutputStream, BufferedAdapterInternalBufferTypes);
|
||||
|
||||
TYPED_TEST(AdapterBufferedOutputStream, WhenBufferOverflowThenWriteBufferAndRemainingDataToStream) {
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_TRUE(this->stream.str().empty());
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
|
||||
}
|
||||
|
||||
TYPED_TEST(AdapterBufferedOutputStream, WhenFlushThenWriteImmediately) {
|
||||
uint8_t x{};
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(0));
|
||||
this->writer.flush();
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(1));
|
||||
this->writer.flush();
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(AdapterBufferedOutputStream, WhenBufferIsStackAllocatedThenBufferSizeViaCtorHasNoEffect) {
|
||||
|
||||
//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}};
|
||||
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
w.template writeBytes<1>(x);
|
||||
static constexpr bool ShouldWriteToStream = bitsery::traits::ContainerTraits<typename TestFixture::Buffer>::isResizable;
|
||||
EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream));
|
||||
}
|
||||
@@ -1,180 +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.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
uint32_t b;
|
||||
int16_t c;
|
||||
uint8_t d;
|
||||
int8_t e;
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(DataReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
TEST(DataReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
br.readBytes<1>(a);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
|
||||
TEST(DataReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.b = 94545646;
|
||||
data.c = -8778;
|
||||
data.d = 200;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
IntegralTypes res;
|
||||
br.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br1.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
}
|
||||
|
||||
TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
int32_t c;
|
||||
bpr.readBytes<4>(c);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
|
||||
int16_t r1= {-645};
|
||||
uint32_t r2[2] = {54898,87854};
|
||||
uint8_t r3 = 0xFF;
|
||||
|
||||
bpr.readBytes<2>(r1);
|
||||
bpr.readBuffer<4>(r2, 2);
|
||||
bpr.readBits(r3, 7);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
EXPECT_THAT(r2[0], Eq(0u));
|
||||
EXPECT_THAT(r2[1], Eq(0u));
|
||||
EXPECT_THAT(r3, Eq(0u));
|
||||
}
|
||||
|
||||
TEST(DataReading, ConstBufferAllAdapters) {
|
||||
//create and write to buffer
|
||||
uint16_t data = 7549;
|
||||
Buffer bufWrite{};
|
||||
Writer 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()}};
|
||||
|
||||
uint16_t res1{};
|
||||
r1.readBytes<2>(res1);
|
||||
|
||||
uint16_t res2{};
|
||||
r2.readBytes<2>(res2);
|
||||
EXPECT_THAT(res1, Eq(data));
|
||||
EXPECT_THAT(res2, Eq(data));
|
||||
}
|
||||
@@ -1,137 +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.
|
||||
|
||||
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
|
||||
using testing::Eq;
|
||||
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;
|
||||
using SessionsEnabledReader = bitsery::AdapterReader<InputAdapter, SessionsEnabledConfig>;
|
||||
|
||||
TEST(DataReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidDataError) {
|
||||
SerializationContext ctx;
|
||||
std::string tmp = "larger text then allowed";
|
||||
ctx.createSerializer().text1b(tmp,100);
|
||||
ctx.createDeserializer().text1b(tmp, 10);
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) {
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value1b(uint8_t{1});
|
||||
ser.value1b(uint8_t{2});
|
||||
bool res{};
|
||||
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));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingAlignHasNonZerosThenInvalidDataError) {
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
uint8_t tmp{0xFF};
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.flush();
|
||||
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataError) {
|
||||
uint8_t tmp{0xFF};
|
||||
Buffer buf{};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.endSession();
|
||||
}
|
||||
bw.flush();
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<1>(tmp);
|
||||
br.beginSession();
|
||||
br.readBytes<1>(tmp);
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
}
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidData) {
|
||||
uint8_t tmp1{0xFF};
|
||||
Buffer buf1{};
|
||||
SessionsEnabledWriter bw1{buf1};
|
||||
bw1.writeBytes<1>(tmp1);
|
||||
bw1.flush();
|
||||
SessionsEnabledReader br1{InputAdapter{buf1.begin(), bw1.writtenBytesCount()}};
|
||||
br1.beginSession();
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
|
||||
Buffer buf2{};
|
||||
SessionsEnabledWriter bw2{buf2};
|
||||
uint16_t tmp2{0x8000};
|
||||
bw2.writeBytes<2>(tmp2);
|
||||
bw2.flush();
|
||||
SessionsEnabledReader br2{InputAdapter{buf2.begin(), bw2.writtenBytesCount()}};
|
||||
br2.beginSession();
|
||||
EXPECT_THAT(br2.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidData) {
|
||||
Buffer buf{};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<4>(uint32_t{10});
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.beginSession();
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingNewSessionOutsideSessionThenInvalidData) {
|
||||
Buffer buf{};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
bw.beginSession();
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.endSession();
|
||||
bw.flush();
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.beginSession();
|
||||
br.endSession();
|
||||
br.beginSession();
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
@@ -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);
|
||||
@@ -384,12 +384,12 @@ TEST(FlexibleSyntax, StdSmartPtr) {
|
||||
|
||||
bitsery::ext::PointerLinkingContext plctx1{};
|
||||
BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::PointerLinkingContext> ctx;
|
||||
ctx.createSerializer(&plctx1).archive(dataShared1, dataWeak1, dataUnique1);
|
||||
ctx.createSerializer(plctx1).archive(dataShared1, dataWeak1, dataUnique1);
|
||||
|
||||
std::shared_ptr<int> resShared1{};
|
||||
std::weak_ptr<int> resWeak1{};
|
||||
std::unique_ptr<std::string> resUnique1{};
|
||||
ctx.createDeserializer(&plctx1).archive(resShared1, resWeak1, resUnique1);
|
||||
ctx.createDeserializer(plctx1).archive(resShared1, resWeak1, resUnique1);
|
||||
//clear shared state from pointer linking context
|
||||
plctx1.clearSharedState();
|
||||
|
||||
|
||||
@@ -182,13 +182,13 @@ TEST(DeserializeNonDefaultConstructible, StdMap) {
|
||||
data.emplace(NonDefaultConstructible{2}, NonDefaultConstructible{3});
|
||||
data.emplace(NonDefaultConstructible{4}, NonDefaultConstructible{4});
|
||||
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, bitsery::ext::StdMap{10},[&ser](NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
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();
|
||||
des.ext(res, bitsery::ext::StdMap{10},[&des](NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
auto des = ctx.createDeserializer();
|
||||
des.ext(res, bitsery::ext::StdMap{10},[](decltype(des)& des, NonDefaultConstructible& key, NonDefaultConstructible& value) {
|
||||
des.object(key);
|
||||
des.object(value);
|
||||
});
|
||||
@@ -223,8 +223,8 @@ TEST(DeserializeNonDefaultConstructible, NonPolymorphicPointerAndSmartPointer) {
|
||||
|
||||
NonPolymorphicPointers res{};
|
||||
bitsery::ext::PointerLinkingContext plctx1{};
|
||||
ctx.createSerializer(&plctx1).object(data);
|
||||
ctx.createDeserializer(&plctx1).object(res);
|
||||
ctx.createSerializer(plctx1).object(data);
|
||||
ctx.createDeserializer(plctx1).object(res);
|
||||
|
||||
EXPECT_THAT(*res.pp, Eq(*data.pp));
|
||||
delete res.pp;
|
||||
@@ -322,8 +322,8 @@ TEST(DeserializeNonDefaultConstructible, PolymorphicPointerAndSmartPointer) {
|
||||
std::get<1>(serCtx).registerBasesList<typename SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
|
||||
std::get<1>(desCtx).registerBasesList<typename SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<PolymorphicNDCBase>{});
|
||||
|
||||
ctx.createSerializer(&serCtx).object(data);
|
||||
ctx.createDeserializer(&desCtx).object(res);
|
||||
ctx.createSerializer(serCtx).object(data);
|
||||
ctx.createDeserializer(desCtx).object(res);
|
||||
auto respp = dynamic_cast<PolymorphicNDC1*>(res.pp);
|
||||
auto resup = dynamic_cast<PolymorphicNDC2*>(res.up.get());
|
||||
auto ressp = dynamic_cast<PolymorphicNDC1*>(res.sp.get());
|
||||
|
||||
@@ -38,12 +38,12 @@ TEST(SerializeBooleans, BoolAsBit) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto& ser = ctx.createSerializer();
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.enableBitPacking([&t1, &t2](Serializer& sbp) {
|
||||
sbp.boolValue(t1);
|
||||
sbp.boolValue(t2);
|
||||
});
|
||||
auto& des = ctx.createDeserializer();
|
||||
auto des = ctx.createDeserializer();
|
||||
des.enableBitPacking([&res1, &res2](Deserializer& sbp) {
|
||||
sbp.boolValue(res1);
|
||||
sbp.boolValue(res2);
|
||||
@@ -60,10 +60,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);
|
||||
|
||||
@@ -71,3 +71,17 @@ TEST(SerializeBooleans, BoolAsByte) {
|
||||
EXPECT_THAT(res2, Eq(t2));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(2));
|
||||
}
|
||||
|
||||
TEST(SerializeBooleans, WhenReadingBoolByteReadsMoreThanOneThenInvalidDataErrorAndResultIsFalse) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.value1b(uint8_t{1});
|
||||
ser.value1b(uint8_t{2});
|
||||
bool res{};
|
||||
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));
|
||||
}
|
||||
@@ -64,6 +64,13 @@ std::list<MyStruct2> getFilledContainer<std::list<MyStruct2>>() {
|
||||
};
|
||||
}
|
||||
|
||||
struct EmptyFtor {
|
||||
template <typename S, typename T>
|
||||
void operator() (S& , T& ) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* start testing session
|
||||
*/
|
||||
@@ -106,12 +113,12 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements
|
||||
SerializationContext ctx{};
|
||||
using TValue = typename TestFixture::TValue;
|
||||
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(this->src, 1000, [&ser](TValue& v) {
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.container(this->src, 1000, [](decltype(ser)& ser, TValue& v) {
|
||||
ser.template value<sizeof(v)>(v);
|
||||
});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(this->res, 1000, [&des](TValue &v) {
|
||||
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
|
||||
v++;
|
||||
@@ -161,9 +168,8 @@ TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, CustomFunctionThatDoNoth
|
||||
SerializationContext ctx{};
|
||||
using TValue = typename TestFixture::TValue;
|
||||
|
||||
auto emptyFnc = [](TValue &) {};
|
||||
ctx.createSerializer().container(this->src, 1000, emptyFnc);
|
||||
ctx.createDeserializer().container(this->res, 1000, emptyFnc);
|
||||
ctx.createSerializer().container(this->src, 1000, EmptyFtor{});
|
||||
ctx.createDeserializer().container(this->res, 1000, EmptyFtor{});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(this->src.size())));
|
||||
}
|
||||
@@ -231,14 +237,14 @@ TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializ
|
||||
using TValue = decltype(*std::begin(res));
|
||||
|
||||
SerializationContext ctx{};
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(src, [&ser](TValue &v) {
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.container(src, [](decltype(ser)& ser, TValue &v) {
|
||||
char tmp{};
|
||||
ser.object(v);
|
||||
ser.value1b(tmp);
|
||||
});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(res, [&des](TValue &v) {
|
||||
auto des = ctx.createDeserializer();
|
||||
des.container(res, [](decltype(des)& des, TValue &v) {
|
||||
char tmp{};
|
||||
des.object(v);
|
||||
des.value1b(tmp);
|
||||
@@ -253,12 +259,11 @@ class SerializeContainer : public ::testing::TestWithParam<size_t> {
|
||||
|
||||
TEST_P(SerializeContainer, SizeHasVariableLength) {
|
||||
SerializationContext ctx{};
|
||||
auto emptyFnc = [](uint8_t &) {};
|
||||
|
||||
std::vector<uint8_t > src(GetParam());
|
||||
std::vector<uint8_t > res{};
|
||||
ctx.createSerializer().container(src, std::numeric_limits<size_t>::max(), emptyFnc);
|
||||
ctx.createDeserializer().container(res, std::numeric_limits<size_t>::max(), emptyFnc);
|
||||
ctx.createSerializer().container(src, std::numeric_limits<size_t>::max(), EmptyFtor{});
|
||||
ctx.createDeserializer().container(res, std::numeric_limits<size_t>::max(), EmptyFtor{});
|
||||
|
||||
EXPECT_THAT(res.size(), Eq(src.size()));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
|
||||
|
||||
@@ -26,136 +26,83 @@
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
template <typename ... Args>
|
||||
struct ConfigWithContext: bitsery::DefaultConfig {
|
||||
using InternalContext = std::tuple<Args...>;
|
||||
};
|
||||
|
||||
template <typename Context, typename ... Args>
|
||||
using SerializerConfigWithContext = bitsery::BasicSerializer<
|
||||
bitsery::AdapterWriter<bitsery::OutputBufferAdapter<Buffer>, ConfigWithContext<Args...>>, Context>;
|
||||
|
||||
template <typename Context, typename ... Args>
|
||||
using DeserializerConfigWithContext = bitsery::BasicDeserializer<
|
||||
bitsery::AdapterReader<bitsery::InputBufferAdapter<Buffer>, ConfigWithContext<Args...>>, Context>;
|
||||
|
||||
template <typename Context>
|
||||
using MySerializer = bitsery::BasicSerializer<Writer, Context>;
|
||||
|
||||
template <typename Context>
|
||||
using MyDeserializer = bitsery::BasicDeserializer<Reader, Context>;
|
||||
using bitsery::DefaultConfig;
|
||||
|
||||
using SingleTypeContext = int;
|
||||
using MultipleTypesContext = std::tuple<int, float, char>;
|
||||
|
||||
TEST(SerializationContext, WhenUsingContextThenReturnsUnderlyingPointerOrNull) {
|
||||
Buffer buf{};
|
||||
MySerializer<SingleTypeContext> ser1{buf, nullptr};
|
||||
EXPECT_THAT(ser1.context(), ::testing::IsNull());
|
||||
|
||||
MySerializer<MultipleTypesContext> ser2{buf, nullptr};
|
||||
EXPECT_THAT(ser2.context(), ::testing::IsNull());
|
||||
|
||||
SingleTypeContext sctx{};
|
||||
MyDeserializer<SingleTypeContext> des1{InputAdapter{buf.begin(), 1}, &sctx};
|
||||
EXPECT_THAT(des1.context(), Eq(&sctx));
|
||||
|
||||
MultipleTypesContext mctx{};
|
||||
MyDeserializer<MultipleTypesContext> des2{InputAdapter{buf.begin(), 1}, &mctx};
|
||||
EXPECT_THAT(des2.context(), Eq(&mctx));
|
||||
TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) {
|
||||
SingleTypeContext ctx{54};
|
||||
BasicSerializationContext<DefaultConfig, SingleTypeContext> c1;
|
||||
auto ser1 = c1.createSerializer(ctx);
|
||||
|
||||
EXPECT_THAT(ser1.context<SingleTypeContext>(), Eq(ctx));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, WhenContextIsTupleThenContextCastOverloadCastsToIndividualTupleTypes) {
|
||||
Buffer buf{};
|
||||
MySerializer<MultipleTypesContext> ser1{buf, nullptr};
|
||||
EXPECT_THAT(ser1.context<int>(), ::testing::IsNull());
|
||||
EXPECT_THAT(ser1.context<float>(), ::testing::IsNull());
|
||||
EXPECT_THAT(ser1.context<char>(), ::testing::IsNull());
|
||||
TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) {
|
||||
|
||||
MultipleTypesContext ctx{5, 798.654, 'F'};
|
||||
BasicSerializationContext<DefaultConfig, 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));
|
||||
EXPECT_THAT(ser1.context<char>(), std::get<2>(ctx));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, WhenContextIsNotTupleThenContextCastOverloadReturnSameType) {
|
||||
Buffer buf{};
|
||||
SingleTypeContext ctx{};
|
||||
MySerializer<SingleTypeContext> ser1{buf, &ctx};
|
||||
EXPECT_THAT(ser1.context<SingleTypeContext>(), Eq(&ctx));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, SerializerDeserializerCanHaveInternalContextViaConfig) {
|
||||
Buffer buf{};
|
||||
SerializerConfigWithContext<void, float, int> ser{buf};
|
||||
EXPECT_THAT(ser.context<int>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*ser.context<int>(), Eq(0));
|
||||
*ser.context<int>() = 10;
|
||||
EXPECT_THAT(*ser.context<int>(), Eq(10));
|
||||
|
||||
DeserializerConfigWithContext<void, char> des{InputAdapter{buf.begin(), 1}};
|
||||
EXPECT_THAT(des.context<char>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*des.context<char>(), Eq(0));
|
||||
*des.context<char>() = 10;
|
||||
EXPECT_THAT(*des.context<char>(), Eq(10));
|
||||
|
||||
//new instance has new context
|
||||
SerializerConfigWithContext<void, float, int> ser2{buf};
|
||||
EXPECT_THAT(ser2.context<int>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*ser2.context<int>(), Eq(0));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, WhenInternalAndExternalContextIsTheSamePriorityGoesToInternalContext) {
|
||||
Buffer buf{};
|
||||
int externalCtx = 5;
|
||||
|
||||
SerializerConfigWithContext<int, float, int> ser{buf, &externalCtx};
|
||||
EXPECT_THAT(ser.context<int>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*ser.context<int>(), Eq(0));
|
||||
*ser.context<int>() = 2;
|
||||
|
||||
DeserializerConfigWithContext<int, int, char> des{InputAdapter{buf.begin(), 1}, &externalCtx};
|
||||
EXPECT_THAT(des.context<char>(), ::testing::NotNull());
|
||||
EXPECT_THAT(*des.context<char>(), Eq(0));
|
||||
*des.context<int>() = 3;
|
||||
|
||||
EXPECT_THAT(externalCtx, Eq(5));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, ContextIfExistsReturnsNullWhenTypeDoesntExists) {
|
||||
Buffer buf{};
|
||||
std::tuple<double, short> extCtx1{};
|
||||
|
||||
SerializerConfigWithContext<std::tuple<double, short>, float, int> ser{buf, &extCtx1};
|
||||
EXPECT_THAT(ser.contextOrNull<int>(), ::testing::NotNull());
|
||||
TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull) {
|
||||
SingleTypeContext ctx1= 32;
|
||||
BasicSerializationContext<DefaultConfig, 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));
|
||||
|
||||
double extCtx2{};
|
||||
DeserializerConfigWithContext<double, int, char> des{InputAdapter{buf.begin(), 1}, &extCtx2};
|
||||
EXPECT_THAT(des.contextOrNull<double>(), ::testing::NotNull());
|
||||
EXPECT_THAT(des.contextOrNull<float>(), ::testing::IsNull());
|
||||
MultipleTypesContext ctx2{5, 798.654, 'F'};
|
||||
BasicSerializationContext<DefaultConfig, 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'));
|
||||
EXPECT_THAT(*des.contextOrNull<int>(), Eq(5));
|
||||
}
|
||||
|
||||
TEST(SerializationContext, WhenBitPackingIsEnabledThenInternalContextIsMovedToNewInstanceAndMovedBackAfterwards) {
|
||||
Buffer buf{};
|
||||
using Ser = SerializerConfigWithContext<void, int>;
|
||||
using BPSer = typename Ser::BPEnabledType;
|
||||
|
||||
using Des = DeserializerConfigWithContext<void, int>;
|
||||
using BPDes = typename Des::BPEnabledType;
|
||||
|
||||
Ser ser{buf, nullptr};
|
||||
*ser.context<int>() = 1;
|
||||
EXPECT_THAT(*ser.context<int>(), Eq(1));
|
||||
ser.enableBitPacking([](BPSer& s) {
|
||||
EXPECT_THAT(*s.context<int>(), Eq(1));
|
||||
*s.context<int>() = 2;
|
||||
});
|
||||
EXPECT_THAT(*ser.context<int>(), Eq(2));
|
||||
|
||||
Des des{InputAdapter{buf.begin(), 1}, nullptr};
|
||||
*des.context<int>() = 3;
|
||||
EXPECT_THAT(*des.context<int>(), Eq(3));
|
||||
des.enableBitPacking([](BPDes& d) {
|
||||
EXPECT_THAT(*d.context<int>(), Eq(3));
|
||||
*d.context<int>() = 4;
|
||||
});
|
||||
EXPECT_THAT(*des.context<int>(), Eq(4));
|
||||
struct Base { int value{}; };
|
||||
struct Derived: Base{};
|
||||
|
||||
TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible) {
|
||||
Derived ctx1{};
|
||||
BasicSerializationContext<DefaultConfig, Derived> c1;
|
||||
auto ser = c1.createSerializer(ctx1);
|
||||
EXPECT_THAT(ser.contextOrNull<Derived>(), ::testing::NotNull());
|
||||
EXPECT_THAT(ser.contextOrNull<Base>(), ::testing::NotNull());
|
||||
ser.context<Derived>();
|
||||
ser.context<Base>();
|
||||
}
|
||||
|
||||
TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTaken) {
|
||||
{
|
||||
using CTX1 = std::tuple<Base, int, Derived>;
|
||||
CTX1 ctx1{};
|
||||
std::get<0>(ctx1).value = 1;
|
||||
std::get<2>(ctx1).value = 2;
|
||||
BasicSerializationContext<DefaultConfig, 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));
|
||||
}
|
||||
|
||||
{
|
||||
using CTX2 = std::tuple<float, Derived, Base>;
|
||||
CTX2 ctx2{};
|
||||
std::get<1>(ctx2).value = 1;
|
||||
std::get<2>(ctx2).value = 2;
|
||||
BasicSerializationContext<DefaultConfig, 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
|
||||
EXPECT_THAT(des.context<Base>().value, Eq(std::get<1>(ctx2).value));
|
||||
}
|
||||
}
|
||||
@@ -228,13 +228,10 @@ TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
|
||||
SerializationContext ctx;
|
||||
auto data = getValue<uint32_t >(true, 17);
|
||||
uint16_t res{};
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, CompactValueAsObject{});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.ext(res, CompactValueAsObject{});
|
||||
auto& rd = bitsery::AdapterAccess::getReader(des);
|
||||
ctx.createSerializer().ext(data, CompactValueAsObject{});
|
||||
ctx.createDeserializer().ext(res, CompactValueAsObject{});
|
||||
EXPECT_THAT(data, ::testing::Ne(res));
|
||||
EXPECT_THAT(rd.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBefore
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
auto serLambda = [&rangeForValue](BPSer& ser, MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
@@ -136,7 +136,7 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBefore
|
||||
});
|
||||
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
auto desLambda = [&rangeForValue](BPDes& des, MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
@@ -161,14 +161,14 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeDa
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
auto serLambda = [&rangeForValue](BPSer& ser, MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, true), serLambda);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
auto desLambda = [&rangeForValue](BPDes& des, MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
@@ -189,10 +189,10 @@ TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked)
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext(v, Entropy<std::list<MyStruct1>>{values}, [](MyStruct1& ) {});
|
||||
ser.ext(v, Entropy<std::list<MyStruct1>>{values}, [](BPSer& ,MyStruct1& ) {});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext(res, Entropy<std::list<MyStruct1>>{values}, []( MyStruct1& ) {});
|
||||
des.ext(res, Entropy<std::list<MyStruct1>>{values}, [](BPDes& , MyStruct1& ) {});
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
|
||||
@@ -32,114 +32,73 @@ struct DataV1 {
|
||||
int32_t v1;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, DataV1& o) {
|
||||
s.value4b(o.v1);
|
||||
}
|
||||
|
||||
struct DataV2 {
|
||||
int32_t v1;
|
||||
int32_t v2;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, DataV2& o) {
|
||||
s.value4b(o.v1);
|
||||
s.value4b(o.v2);
|
||||
}
|
||||
|
||||
struct DataV3 {
|
||||
int32_t v1;
|
||||
int32_t v2;
|
||||
int32_t v3;
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value4b(v1);
|
||||
s.value4b(v2);
|
||||
s.value4b(v3);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
TEST(SerializeExtensionGrowable, SessionsLengthIsStoredWith4BytesBeforeSessionDataStarts) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
//session cannot be empty
|
||||
ser.ext(int8_t{}, Growable{}, [&ser] (int8_t& v) {
|
||||
ser.value2b(int16_t{1});
|
||||
ser.ext(int8_t{2}, Growable{}, [] (decltype(ser)& ser, int8_t& v) {
|
||||
ser.value1b(v);
|
||||
});
|
||||
ser.value1b(int8_t{3});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1u));
|
||||
ctx.bw->flush();
|
||||
EXPECT_THAT(ctx.getBufferSize(), Gt(1u));
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSessionsDataOffset) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
|
||||
|
||||
constexpr size_t DATA_SIZE = 4;
|
||||
int32_t data{};
|
||||
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, Growable{}, [&ser](int32_t & v) { ser.value4b(v);});
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1+4 + DATA_SIZE));
|
||||
|
||||
//read value back
|
||||
auto& br = *(ctx.br);
|
||||
br.readBytes<DATA_SIZE>(data);
|
||||
|
||||
size_t sessionEnd{};
|
||||
//there should start session data with first size of session
|
||||
bitsery::details::readSize(br, sessionEnd, 1000000u);
|
||||
EXPECT_THAT(sessionEnd, Eq(DATA_SIZE));
|
||||
//this is the the offset from the end of buffer where actual data ends
|
||||
uint32_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<4>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+4));//1byte for session info, 4 bytes for session offset variable
|
||||
auto writtenSize = ctx.bw->writtenBytesCount();
|
||||
auto dSize = writtenSize - sessionsOffset;
|
||||
EXPECT_THAT(dSize, Eq(DATA_SIZE));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
DataV3 data{19457,846, 498418};
|
||||
ctx.createSerializer();
|
||||
ctx.bw->beginSession();
|
||||
ctx.bw->writeBytes<4>(data.v1);
|
||||
ctx.bw->beginSession();
|
||||
ctx.bw->writeBytes<4>(data.v2);
|
||||
ctx.bw->endSession();
|
||||
ctx.bw->beginSession();
|
||||
ctx.bw->writeBytes<4>(data.v3);
|
||||
ctx.bw->endSession();
|
||||
ctx.bw->endSession();
|
||||
DataV3 res{};
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
ctx.br->readBytes<4>(res.v1);
|
||||
ctx.br->readBytes<4>(res.v2);
|
||||
ctx.br->readBytes<4>(res.v3);
|
||||
//read data correctly
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
EXPECT_THAT(res.v3, Eq(data.v3));
|
||||
size_t sessionEnd[3];
|
||||
//read sessions sizes
|
||||
bitsery::details::readSize(*(ctx.br), sessionEnd[0],10000000u);
|
||||
bitsery::details::readSize(*(ctx.br), sessionEnd[1],10000000u);
|
||||
bitsery::details::readSize(*(ctx.br), sessionEnd[2],10000000u);
|
||||
EXPECT_THAT(sessionEnd[0], Eq(12));
|
||||
EXPECT_THAT(sessionEnd[1], Eq(8));
|
||||
EXPECT_THAT(sessionEnd[2], Eq(12));
|
||||
auto des = ctx.createDeserializer();
|
||||
uint8_t res1b{};
|
||||
uint16_t res2b{};
|
||||
uint32_t res4b{};
|
||||
des.value2b(res2b);
|
||||
EXPECT_THAT(res2b, Eq(1));
|
||||
des.value4b(res4b);
|
||||
EXPECT_THAT(res4b, Eq(1+4));//size + 4bytes
|
||||
des.value1b(res1b);
|
||||
EXPECT_THAT(res1b, Eq(2));
|
||||
des.value1b(res1b);
|
||||
EXPECT_THAT(res1b, Eq(3));
|
||||
EXPECT_THAT(ctx.bw->writtenBytesCount(), Eq(8));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
auto ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
auto des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
br.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
@@ -147,53 +106,37 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
SerializationContext ctx;
|
||||
DataV3 data{8454,987451, 45612};
|
||||
auto ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
auto des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
br.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
auto ser = ctx.createSerializer();
|
||||
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(ser, data);
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
DataV3 res{4798,657891,985};
|
||||
auto& br = (*ctx.br);
|
||||
ctx.createDeserializer();
|
||||
DataV3 res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
br.readBytes<4>(res.v3);
|
||||
br.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(des, res);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
@@ -202,225 +145,94 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.endSession();
|
||||
bw.endSession();
|
||||
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);
|
||||
});
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
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));
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
bitsery::FtorExtObject<Growable>{}(des, o);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData) {
|
||||
SerializationContext ctx;
|
||||
DataV3 data{8454,987451, 54124};
|
||||
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);
|
||||
//new fields can only be added at the end
|
||||
ser.value4b(o.v3);
|
||||
});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV2 res{};
|
||||
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);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
SerializationContext ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v2);
|
||||
}
|
||||
auto ser = ctx.createSerializer();
|
||||
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
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);
|
||||
});
|
||||
}
|
||||
ctx.createDeserializer();
|
||||
DataV3 res{};
|
||||
auto& br = (*ctx.br);
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
//new flow
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v3);
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v3);
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v3);
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
br.readBytes<4>(res.v2);
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
{
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
//new flow
|
||||
bw.beginSession();
|
||||
{
|
||||
bw.writeBytes<4>(data.v3);
|
||||
}
|
||||
bw.endSession();
|
||||
bw.beginSession();
|
||||
{
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.beginSession();
|
||||
{
|
||||
bw.beginSession();
|
||||
{
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.writeBytes<4>(data.v3);
|
||||
}
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
}
|
||||
bw.endSession();
|
||||
}
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
}
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v2);
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
{
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
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));
|
||||
}
|
||||
br.endSession();
|
||||
br.readBytes<4>(res.v2);
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
|
||||
//new flow
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.beginSession();
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v2);
|
||||
|
||||
//new flow
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
bw.writeBytes<4>(data.v3);
|
||||
bw.endSession();
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
bitsery::FtorExtObject<Growable>{}(des, o);
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
br.endSession();
|
||||
br.readBytes<4>(res.v2);
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
br.endSession();
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionsStartsAtEndOfSerialization) {
|
||||
BasicSerializationContext<SessionsEnabledConfig, void> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
bw.writeBytes<4>(data.v1);
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<4>(data.v1);
|
||||
bw.writeBytes<4>(data.v2);
|
||||
bw.endSession();
|
||||
}
|
||||
//create more sessions that can fit in 2 bytes
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
DataV2 res{};
|
||||
auto& br = (*ctx.br);
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
br.readBytes<4>(res.v1);
|
||||
for (auto i = 0; i < 10; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<4>(res.v1);
|
||||
br.readBytes<4>(res.v2);
|
||||
br.endSession();
|
||||
EXPECT_THAT(res.v1, Eq(data.v1));
|
||||
EXPECT_THAT(res.v2, Eq(data.v2));
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
//new fields can only be added at the end
|
||||
des.value4b(o.v3);
|
||||
EXPECT_THAT(res.v3, Eq(0));
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true));
|
||||
}
|
||||
|
||||
@@ -27,11 +27,7 @@
|
||||
using bitsery::ext::BaseClass;
|
||||
using bitsery::ext::VirtualBaseClass;
|
||||
|
||||
struct ConfigWithInheritanceCtx:bitsery::DefaultConfig {
|
||||
using InternalContext = std::tuple<bitsery::ext::InheritanceContext>;
|
||||
};
|
||||
|
||||
using SerContext = BasicSerializationContext<ConfigWithInheritanceCtx, void>;
|
||||
using SerContext = BasicSerializationContext<bitsery::DefaultConfig, bitsery::ext::InheritanceContext>;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
@@ -68,7 +64,7 @@ struct Derive2NonVirtually:Base {
|
||||
template <typename S>
|
||||
void serialize(S& s, Derive2NonVirtually& o) {
|
||||
//use lambda to serialize base
|
||||
s.ext(o, BaseClass<Base>{}, [&s](Base& b) {
|
||||
s.ext(o, BaseClass<Base>{}, [](S& s, Base& b) {
|
||||
s.object(b);
|
||||
});
|
||||
s.value1b(o.y2);
|
||||
@@ -140,8 +136,10 @@ TEST(SerializeExtensionInheritance, BaseClass) {
|
||||
Derive1NonVirtually rd1{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(d1);
|
||||
ctx.createDeserializer().object(rd1);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(d1);
|
||||
ctx.createDeserializer(inherCtxDes).object(rd1);
|
||||
|
||||
EXPECT_THAT(rd1.x, Eq(d1.x));
|
||||
EXPECT_THAT(rd1.y1, Eq(d1.y1));
|
||||
@@ -155,8 +153,10 @@ TEST(SerializeExtensionInheritance, VirtualBaseClass) {
|
||||
Derive1Virtually rd1{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(d1);
|
||||
ctx.createDeserializer().object(rd1);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(d1);
|
||||
ctx.createDeserializer(inherCtxDes).object(rd1);
|
||||
|
||||
EXPECT_THAT(rd1.x, Eq(d1.x));
|
||||
EXPECT_THAT(rd1.y1, Eq(d1.y1));
|
||||
@@ -174,8 +174,10 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithoutVirtualInheritance) {
|
||||
MultipleInheritanceNonVirtualBase res{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(md);
|
||||
ctx.createDeserializer().object(res);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(md);
|
||||
ctx.createDeserializer(inherCtxDes).object(res);
|
||||
|
||||
EXPECT_THAT(static_cast<Derive1NonVirtually&>(res).x, Eq(static_cast<Derive1NonVirtually&>(md).x));
|
||||
EXPECT_THAT(static_cast<Derive2NonVirtually&>(res).x, Eq(static_cast<Derive2NonVirtually&>(md).x));
|
||||
@@ -217,8 +219,10 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritance) {
|
||||
MultipleInheritanceVirtualBase res{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(md);
|
||||
ctx.createDeserializer().object(res);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(md);
|
||||
ctx.createDeserializer(inherCtxDes).object(res);
|
||||
EXPECT_THAT(res, Eq(md));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(4)); //4 because virtual base
|
||||
}
|
||||
@@ -233,8 +237,10 @@ TEST(SerializeExtensionInheritance, MultipleBasesWithVirtualInheritanceMultipleO
|
||||
std::vector<MultipleInheritanceVirtualBase> res{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).container(data, 10);
|
||||
ctx.createDeserializer(inherCtxDes).container(res, 10);
|
||||
EXPECT_THAT(res, ::testing::ContainerEq(data));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1 + 4 * data.size())); //1 container size + 4 because virtual base * elements
|
||||
}
|
||||
@@ -264,7 +270,7 @@ public:
|
||||
template <typename S>
|
||||
void serialize(S& s, DerivedPrivateBase& o) {
|
||||
//use lambda for base serialization
|
||||
s.ext(o, BaseClass<BasePrivateSerialize>{}, [&s](BasePrivateSerialize& b) {
|
||||
s.ext(o, BaseClass<BasePrivateSerialize>{}, [](S& s, BasePrivateSerialize& b) {
|
||||
s.object(b);
|
||||
});
|
||||
s.value1b(o.z);
|
||||
@@ -309,10 +315,12 @@ TEST(SerializeExtensionInheritance, WhenDerivedClassHasAmbiguousSerializeFunctio
|
||||
DerivedMemberSerialize res2{};
|
||||
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(data1);
|
||||
ctx.createSerializer().object(data2);
|
||||
ctx.createDeserializer().object(res1);
|
||||
ctx.createDeserializer().object(res2);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(data1);
|
||||
ctx.createSerializer(inherCtxSer).object(data2);
|
||||
ctx.createDeserializer(inherCtxDes).object(res1);
|
||||
ctx.createDeserializer(inherCtxDes).object(res2);
|
||||
EXPECT_THAT(res1.getX(), Eq(data1.getX()));
|
||||
EXPECT_THAT(res1.z, Eq(data1.z));
|
||||
EXPECT_THAT(res2.x, Eq(data2.x));
|
||||
@@ -348,8 +356,10 @@ TEST(SerializeExtensionInheritance, CanSerializeAbstractClass) {
|
||||
data.exec();
|
||||
ImplementedBase res{};
|
||||
SerContext ctx{};
|
||||
ctx.createSerializer().object(data);
|
||||
ctx.createDeserializer().object(res);
|
||||
bitsery::ext::InheritanceContext inherCtxSer{};
|
||||
bitsery::ext::InheritanceContext inherCtxDes{};
|
||||
ctx.createSerializer(inherCtxSer).object(data);
|
||||
ctx.createDeserializer(inherCtxDes).object(res);
|
||||
EXPECT_THAT(res.x, Eq(data.x));
|
||||
EXPECT_THAT(res.y, Eq(data.y));
|
||||
}
|
||||
@@ -60,13 +60,19 @@ public:
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1{};
|
||||
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
return sctx1.createSerializer(&plctx1);
|
||||
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
return sctx1.createSerializer(plctx1);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
return sctx1.createDeserializer(plctx1);
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
return plctx1.isValid();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
@@ -74,15 +80,15 @@ TEST(SerializeExtensionPointer, RequiresPointerLinkingContext) {
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext(data, PointerOwner{});
|
||||
sctx1.createDeserializer(&plctx1).ext(data, PointerOwner{});
|
||||
sctx1.createSerializer(plctx1).ext(data, PointerOwner{});
|
||||
sctx1.createDeserializer(plctx1).ext(data, PointerOwner{});
|
||||
|
||||
//linking context in tuple
|
||||
using ContextInTuple = std::tuple<int, PointerLinkingContext, float, char>;
|
||||
ContextInTuple plctx2(0, PointerLinkingContext{}, 0.0f, 'a');
|
||||
BasicSerializationContext<SessionsEnabledConfig, ContextInTuple> sctx2;
|
||||
sctx2.createSerializer(&plctx2).ext(data, PointerObserver{});
|
||||
sctx2.createDeserializer(&plctx2).ext(data, PointerObserver{});
|
||||
BasicSerializationContext<bitsery::DefaultConfig, ContextInTuple> sctx2;
|
||||
sctx2.createSerializer(plctx2).ext(data, PointerObserver{});
|
||||
sctx2.createDeserializer(plctx2).ext(data, PointerObserver{});
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionPointer, PointerLinkingContextAcceptsMultipleSharedOwnersAndReturnSameId) {
|
||||
@@ -115,12 +121,12 @@ 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{});
|
||||
sctx1.createDeserializer();
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
|
||||
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
@@ -128,18 +134,9 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointersAreNullThenIsValid) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
TEST(SerializeExtensionPointer, WhenPointerLinkingContextIsNullAndPointerIsNotNullThenAssert) {
|
||||
MyStruct1 tmp;
|
||||
MyStruct1 *data = &tmp;
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
EXPECT_DEATH(sctx1.createSerializer(nullptr).ext(data, PointerOwner{}), "");
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerOwnerIsNotUniqueThenAssert) {
|
||||
|
||||
auto &ser = createSerializer();
|
||||
auto ser = createSerializer();
|
||||
ser.ext2b(p1null, PointerOwner{});
|
||||
ser.ext2b(pd1, PointerOwner{});
|
||||
ser.ext4b(pd2, PointerOwner{});
|
||||
@@ -149,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{});
|
||||
|
||||
@@ -157,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}), "");
|
||||
}
|
||||
@@ -172,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));
|
||||
@@ -185,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
|
||||
@@ -197,10 +194,10 @@ TEST_F(SerializeExtensionPointerSerialization, ReferenceTypeCanAlsoBeReferencedB
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZero) {
|
||||
auto &ser1 = createSerializer();
|
||||
auto ser1 = createSerializer();
|
||||
ser1.ext(p3null, PointerOwner{});
|
||||
ser1.ext2b(p1null, PointerObserver{});
|
||||
sctx1.createDeserializer();
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2));
|
||||
size_t res;
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
@@ -210,12 +207,12 @@ TEST_F(SerializeExtensionPointerSerialization, WhenPointerIsNullThenPointerIdIsZ
|
||||
}
|
||||
|
||||
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{});
|
||||
sctx1.createDeserializer();
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(4));
|
||||
size_t res;
|
||||
bitsery::details::readSize(*sctx1.br, res, 10000u);
|
||||
@@ -229,20 +226,20 @@ TEST_F(SerializeExtensionPointerSerialization, PointerIdsStartsFromOne) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerObserversDoesntSerializeObject) {
|
||||
auto &ser1 = createSerializer();
|
||||
auto ser1 = createSerializer();
|
||||
ser1.ext2b(pd1, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
ser1.ext4b(pd2, PointerObserver{});
|
||||
sctx1.createDeserializer();
|
||||
createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->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 = sctx1.createDeserializer();
|
||||
auto des = createDeserializer();
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(3 + 6));
|
||||
size_t id{};
|
||||
bitsery::details::readSize(*sctx1.br, id, 10000u);
|
||||
@@ -258,10 +255,10 @@ TEST_F(SerializeExtensionPointerSerialization, ReferencedByPointerSerializesIdAn
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject) {
|
||||
auto &ser1 = createSerializer();
|
||||
auto ser1 = createSerializer();
|
||||
ser1.ext4b(pd2, PointerOwner{});
|
||||
ser1.ext(pd3, PointerOwner{});
|
||||
auto &des1 = sctx1.createDeserializer();
|
||||
auto des1 = createDeserializer();
|
||||
//2x ids + int32_t + MyStruct1
|
||||
EXPECT_THAT(sctx1.bw->writtenBytesCount(), Eq(2 + 4 + MyStruct1::SIZE));
|
||||
size_t id;
|
||||
@@ -276,22 +273,14 @@ TEST_F(SerializeExtensionPointerSerialization, PointerOwnerSerializesIdAndObject
|
||||
class SerializeExtensionPointerDeserialization : public SerializeExtensionPointerSerialization {
|
||||
public:
|
||||
|
||||
typename SerContext::TSerializer &createSerializer() {
|
||||
return sctx1.createSerializer(&plctx1);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer &createDeserializer() {
|
||||
return sctx1.createDeserializer(&plctx1);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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{});
|
||||
@@ -302,10 +291,10 @@ TEST_F(SerializeExtensionPointerDeserialization, ReferencedByPointer) {
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNullPointerThenInvalidPointerError) {
|
||||
auto &ser = createSerializer();
|
||||
auto ser = createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
ser.ext2b(d1, ReferencedByPointer{});
|
||||
auto &des = createDeserializer();
|
||||
auto des = createDeserializer();
|
||||
des.ext2b(r1, ReferencedByPointer{});
|
||||
EXPECT_THAT(sctx1.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
|
||||
}
|
||||
@@ -313,21 +302,21 @@ TEST_F(SerializeExtensionPointerDeserialization, WhenReferencedByPointerReadsNul
|
||||
TEST_F(SerializeExtensionPointerDeserialization, WhenNonNullPointerIsNullThenInvalidPointerError) {
|
||||
createSerializer();
|
||||
bitsery::details::writeSize(*sctx1.bw, 0u);
|
||||
auto &des1 = createDeserializer();
|
||||
auto des1 = createDeserializer();
|
||||
des1.ext2b(p1null, PointerOwner{PointerType::NotNull});
|
||||
EXPECT_THAT(sctx1.br->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));
|
||||
}
|
||||
|
||||
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{});
|
||||
@@ -342,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{};
|
||||
@@ -362,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{});
|
||||
@@ -370,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{});
|
||||
@@ -399,7 +388,7 @@ struct Test1Data {
|
||||
template<typename S>
|
||||
void serialize(S &s) {
|
||||
//set container elements to be candidates for non-owning pointers
|
||||
s.container(vdata, 100, [&s](MyStruct1 &d) {
|
||||
s.container(vdata, 100, [](S& s, MyStruct1 &d) {
|
||||
s.ext(d, ReferencedByPointer{});
|
||||
});
|
||||
//contains non owning pointers
|
||||
@@ -407,7 +396,7 @@ struct Test1Data {
|
||||
//IMPORTANT !!!
|
||||
// ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
|
||||
//
|
||||
s.container(vptr, 100, [&s](MyStruct1 *(&d)) {
|
||||
s.container(vptr, 100, [](S& s, MyStruct1 *(&d)) {
|
||||
s.ext(d, PointerObserver{});
|
||||
});
|
||||
//just a regular fields
|
||||
@@ -443,8 +432,8 @@ TEST(SerializeExtensionPointer, IntegrationTest) {
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).object(data);
|
||||
sctx1.createDeserializer(&plctx1).object(res);
|
||||
sctx1.createSerializer(plctx1).object(data);
|
||||
sctx1.createDeserializer(plctx1).object(res);
|
||||
|
||||
EXPECT_THAT(plctx1.isValid(), Eq(true));
|
||||
//check regular fields
|
||||
@@ -474,13 +463,13 @@ TEST(SerializeExtensionPointer, PointerOwnerWithNonPolymorphicTypeCanUseLambdaOv
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, PointerOwner{}, [&ser](MyStruct1 &o) {
|
||||
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);
|
||||
des.ext(res, PointerOwner{}, [&des](MyStruct1 &o) {
|
||||
auto des = sctx1.createDeserializer(plctx1);
|
||||
des.ext(res, PointerOwner{}, [](decltype(des)& des,MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
@@ -500,13 +489,13 @@ TEST(SerializeExtensionPointer, ReferencedByPointerCanUseLambdaOverload) {
|
||||
//linking context
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
auto &ser = sctx1.createSerializer(&plctx1);
|
||||
ser.ext(data, ReferencedByPointer{}, [&ser](MyStruct1 &o) {
|
||||
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);
|
||||
des.ext(res, ReferencedByPointer{}, [&des](MyStruct1 &o) {
|
||||
auto des = sctx1.createDeserializer(plctx1);
|
||||
des.ext(res, ReferencedByPointer{}, [](decltype(des)& des,MyStruct1 &o) {
|
||||
//deserialize only one field
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
@@ -521,8 +510,8 @@ TEST(SerializeExtensionPointer, PointerOwnerCanUseValueOverload) {
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, PointerOwner{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, PointerOwner{});
|
||||
sctx1.createSerializer(plctx1).ext8b(data, PointerOwner{});
|
||||
sctx1.createDeserializer(plctx1).ext8b(res, PointerOwner{});
|
||||
|
||||
EXPECT_THAT(*res, Eq(*data));
|
||||
|
||||
@@ -536,8 +525,8 @@ TEST(SerializeExtensionPointer, ReferencedByPointerCanUseValueOverload) {
|
||||
|
||||
PointerLinkingContext plctx1{};
|
||||
SerContext sctx1;
|
||||
sctx1.createSerializer(&plctx1).ext8b(data, ReferencedByPointer{});
|
||||
sctx1.createDeserializer(&plctx1).ext8b(res, ReferencedByPointer{});
|
||||
sctx1.createSerializer(plctx1).ext8b(data, ReferencedByPointer{});
|
||||
sctx1.createDeserializer(plctx1).ext8b(res, ReferencedByPointer{});
|
||||
|
||||
EXPECT_THAT(res, Eq(data));
|
||||
}
|
||||
|
||||
@@ -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,7 +319,7 @@ 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>{});
|
||||
|
||||
@@ -157,7 +157,7 @@ struct MemResourceForTest : public bitsery::ext::MemResourceBase {
|
||||
return res;
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) override {
|
||||
void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) noexcept override {
|
||||
deallocs.push_back({ptr, bytes, alignment, typeId});
|
||||
bitsery::ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
|
||||
}
|
||||
@@ -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>(
|
||||
@@ -202,7 +202,7 @@ public:
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, CanSetDefaultMemoryResourceInPointerLinkingContext) {
|
||||
|
||||
MemResourceForTest memRes{};
|
||||
std::get<0>(plctx).setMemResource(&memRes);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes);
|
||||
|
||||
Base* baseData = new Derived1{2, 1};
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
@@ -225,7 +225,7 @@ TEST_F(SerializeExtensionPointerWithAllocator, CanSetDefaultMemoryResourceInPoin
|
||||
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, CorrectlyDeallocatesPreviousInstance) {
|
||||
MemResourceForTest memRes{};
|
||||
std::get<0>(plctx).setMemResource(&memRes);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes);
|
||||
|
||||
Base* baseData = new Derived1{2, 1};
|
||||
createSerializer().ext(baseData, PointerOwner{});
|
||||
@@ -252,7 +252,7 @@ TEST_F(SerializeExtensionPointerWithAllocator, CorrectlyDeallocatesPreviousInsta
|
||||
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, DefaultDeleterIsNotUsedForStdUniquePtr) {
|
||||
MemResourceForTest memRes{};
|
||||
std::get<0>(plctx).setMemResource(&memRes);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes);
|
||||
|
||||
std::unique_ptr<Base> baseData{};
|
||||
createSerializer().ext(baseData, StdSmartPtr{});
|
||||
@@ -274,7 +274,7 @@ struct CustomBaseDeleter {
|
||||
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, CustomDeleterIsUsedForStdUniquePtr) {
|
||||
MemResourceForTest memRes{};
|
||||
std::get<0>(plctx).setMemResource(&memRes);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes);
|
||||
|
||||
std::unique_ptr<Base, CustomBaseDeleter> baseData{};
|
||||
createSerializer().ext(baseData, StdSmartPtr{});
|
||||
@@ -289,7 +289,7 @@ TEST_F(SerializeExtensionPointerWithAllocator, CustomDeleterIsUsedForStdUniquePt
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer) {
|
||||
MemResourceForTest memRes1{};
|
||||
MemResourceForTest memRes2{};
|
||||
std::get<0>(plctx).setMemResource(&memRes1);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes1);
|
||||
|
||||
Base* baseData = new Derived1{2, 1};
|
||||
createSerializer().ext(baseData, PointerOwner{bitsery::ext::PointerType::Nullable, &memRes2});
|
||||
@@ -320,7 +320,7 @@ TEST_F(SerializeExtensionPointerWithAllocator, CanSetMemResourcePerPointer) {
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerByDefaultDoNotPropagate) {
|
||||
MemResourceForTest memRes1{};
|
||||
MemResourceForTest memRes2{};
|
||||
std::get<0>(plctx).setMemResource(&memRes1);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes1);
|
||||
|
||||
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
|
||||
data->ptr = std::unique_ptr<Base>(new Derived1{5, 6});
|
||||
@@ -343,7 +343,7 @@ TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerByDefault
|
||||
TEST_F(SerializeExtensionPointerWithAllocator, MemResourceSetPerPointerCanPropagate) {
|
||||
MemResourceForTest memRes1{};
|
||||
MemResourceForTest memRes2{};
|
||||
std::get<0>(plctx).setMemResource(&memRes1);
|
||||
std::get<0>(plctx).getAllocator().setMemResource(&memRes1);
|
||||
|
||||
auto data = std::unique_ptr<PolyPtrWithPolyPtrBase>(new PolyPtrWithPolyPtrBase{});
|
||||
data->ptr = std::unique_ptr<Base>(new Derived1{5, 6});
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::unordered_map<std::string, MyStruct1>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](std::string& key, MyStruct1& value) {
|
||||
s.ext(o, StdMap{10}, [](S& s, std::string& key, MyStruct1& value) {
|
||||
s.text1b(key, 100);
|
||||
s.object(value);
|
||||
});
|
||||
@@ -103,7 +103,7 @@ namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::unordered_multimap<int32_t, float>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](int32_t& key, float& value) {
|
||||
s.ext(o, StdMap{10}, [](S& s, int32_t& key, float& value) {
|
||||
s.value4b(key);
|
||||
s.value4b(value);
|
||||
});
|
||||
@@ -111,7 +111,7 @@ namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::map<MyEnumClass , MyStruct1>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](MyEnumClass& key, MyStruct1& value) {
|
||||
s.ext(o, StdMap{10}, [](S& s, MyEnumClass& key, MyStruct1& value) {
|
||||
s.value4b(key);
|
||||
s.object(value);
|
||||
});
|
||||
@@ -119,7 +119,7 @@ namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::multimap<int32_t ,int64_t>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](int32_t& key, int64_t& value) {
|
||||
s.ext(o, StdMap{10}, [](S& s, int32_t& key, int64_t& value) {
|
||||
s.enableBitPacking([&key, &value](typename S::BPEnabledType& sbp) {
|
||||
int64_t values[3]{1ll, 2ll, 3ll};
|
||||
sbp.ext(key, bitsery::ext::ValueRange<int32_t>{-100,100});
|
||||
|
||||
@@ -84,12 +84,12 @@ TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
|
||||
ser.ext(t1, StdOptional(true), [&ser, &range](int32_t& v) {
|
||||
ser.ext(t1, StdOptional(true), [&range](BPSer& ser, int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(true), [&des, &range](int32_t& v) {
|
||||
des.ext(r1, StdOptional(true), [&range](BPDes& des, int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
@@ -105,12 +105,12 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
ser.ext(t1, StdOptional(false), [&ser, &range](int32_t& v) {
|
||||
ser.ext(t1, StdOptional(false), [&range](BPSer& ser, int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(false), [&des, &range](int32_t& v) {
|
||||
des.ext(r1, StdOptional(false), [&range](BPDes& des, int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,12 +66,12 @@ 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();
|
||||
ser.ext(t1, StdSet{10}, [&ser](int32_t& v) {
|
||||
auto ser = ctx1.createSerializer();
|
||||
ser.ext(t1, StdSet{10}, [](decltype(ser)& ser, int32_t& v) {
|
||||
ser.value4b(v);
|
||||
});
|
||||
auto& des = ctx1.createDeserializer();
|
||||
des.ext(r1, StdSet{10}, [&des](int32_t& v) {
|
||||
auto des = ctx1.createDeserializer();
|
||||
des.ext(r1, StdSet{10}, [](decltype(des)& des, int32_t& v) {
|
||||
des.value4b(v);
|
||||
});
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
|
||||
@@ -120,14 +120,12 @@ public:
|
||||
TContext plctx{};
|
||||
SerContext sctx{};
|
||||
|
||||
typename SerContext::TSerializer& createSerializer() {
|
||||
auto& res = sctx.createSerializer(&plctx);
|
||||
return res;
|
||||
typename SerContext::TSerializer createSerializer() {
|
||||
return sctx.createSerializer(plctx);
|
||||
}
|
||||
|
||||
typename SerContext::TDeserializer& createDeserializer() {
|
||||
auto& res = sctx.createDeserializer(&plctx);
|
||||
return res;
|
||||
typename SerContext::TDeserializer createDeserializer() {
|
||||
return sctx.createDeserializer(plctx);
|
||||
}
|
||||
|
||||
bool isPointerContextValid() {
|
||||
@@ -156,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>(
|
||||
@@ -165,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>(
|
||||
@@ -268,14 +266,14 @@ TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, CanUseLambdaOverload
|
||||
using Ext = typename TestFixture::TExt;
|
||||
|
||||
Ptr data{new MyStruct1{3, 78}};
|
||||
auto& ser = this->createSerializer();
|
||||
ser.ext(data, Ext{}, [&ser](MyStruct1& o) {
|
||||
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();
|
||||
des.ext(res, Ext{}, [&des](MyStruct1& o) {
|
||||
auto des = this->createDeserializer();
|
||||
des.ext(res, Ext{}, [](decltype(des)& des, MyStruct1& o) {
|
||||
des.value4b(o.i1);
|
||||
});
|
||||
|
||||
@@ -300,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{});
|
||||
|
||||
@@ -318,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()));
|
||||
@@ -405,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>{});
|
||||
@@ -438,10 +436,10 @@ 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{});
|
||||
auto& des = createDeserializer();
|
||||
createDeserializer();
|
||||
|
||||
//1b linking context (for 1st time)
|
||||
//1b dynamic type info
|
||||
@@ -455,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();
|
||||
@@ -471,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{});
|
||||
@@ -479,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{});
|
||||
@@ -502,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{});
|
||||
@@ -510,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{});
|
||||
@@ -533,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{});
|
||||
@@ -541,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{});
|
||||
@@ -562,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{});
|
||||
|
||||
@@ -585,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{});
|
||||
|
||||
@@ -610,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{});
|
||||
@@ -620,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{});
|
||||
@@ -641,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();
|
||||
@@ -658,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();
|
||||
@@ -676,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());
|
||||
@@ -684,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());
|
||||
|
||||
@@ -46,7 +46,6 @@ TEST(SerializeExtensionStdTuple, ValueTypesCanBeSerializedWithLambdaAndOrCallabl
|
||||
std::tuple<float, int32_t> r1{};
|
||||
SerializationContext ctx;
|
||||
auto exec = [](auto& s, auto& o) {
|
||||
using S = decltype(s);
|
||||
s.ext(o, bitsery::ext::StdTuple{
|
||||
[](auto& s1, float& o1) {
|
||||
s1.value4b(o1);
|
||||
@@ -54,8 +53,8 @@ TEST(SerializeExtensionStdTuple, ValueTypesCanBeSerializedWithLambdaAndOrCallabl
|
||||
OverloadValue<int32_t, 4>{}
|
||||
});
|
||||
};
|
||||
exec(ctx.createSerializer(), t1);
|
||||
exec(ctx.createDeserializer(), r1);
|
||||
ctx.createSerializer().object(t1, exec);
|
||||
ctx.createDeserializer().object(r1, exec);
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
@@ -64,7 +63,6 @@ TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction) {
|
||||
std::tuple<MyStruct1, MyStruct2> r1{};
|
||||
SerializationContext ctx;
|
||||
auto exec = [](auto& s, auto& o) {
|
||||
using S = decltype(s);
|
||||
s.ext(o, bitsery::ext::StdTuple{
|
||||
[](auto& s1, MyStruct1& o1) {
|
||||
s1.value4b(o1.i1);
|
||||
@@ -72,8 +70,8 @@ TEST(SerializeExtensionStdTuple, CanOverloadDefaultSerializeFunction) {
|
||||
},
|
||||
});
|
||||
};
|
||||
exec(ctx.createSerializer(), t1);
|
||||
exec(ctx.createDeserializer(), r1);
|
||||
ctx.createSerializer().object(t1, exec);
|
||||
ctx.createDeserializer().object(r1, exec);
|
||||
EXPECT_THAT(std::get<1>(t1), Eq(std::get<1>(r1)));
|
||||
EXPECT_THAT(std::get<0>(t1).i1, Eq(std::get<0>(r1).i1));
|
||||
EXPECT_THAT(std::get<0>(t1).i2, ::testing::Ne(std::get<0>(r1).i2));
|
||||
|
||||
@@ -77,10 +77,8 @@ TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambdaAndOrCalla
|
||||
s.value4b(v);
|
||||
};
|
||||
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
|
||||
ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
|
||||
ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue<char, 1>{}});
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
@@ -99,8 +97,8 @@ TEST(SerializeExtensionStdVariant, CanOverloadDefaultSerializationFunction) {
|
||||
});
|
||||
};
|
||||
|
||||
exec(ctx.createSerializer(), t1);
|
||||
exec(ctx.createDeserializer(), r1);
|
||||
ctx.createSerializer().object(t1, exec);
|
||||
ctx.createDeserializer().object(r1, exec);
|
||||
EXPECT_THAT(std::get<1>(r1).i2, Eq(0));
|
||||
}
|
||||
|
||||
@@ -135,8 +133,8 @@ TEST(SerializeExtensionStdVariant, CanUseNonDefaultConstructableTypes) {
|
||||
});
|
||||
};
|
||||
|
||||
exec(ctx.createSerializer(), t1);
|
||||
exec(ctx.createDeserializer(), r1);
|
||||
ctx.createSerializer().object(t1, exec);
|
||||
ctx.createDeserializer().object(r1, exec);
|
||||
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
@@ -156,8 +154,8 @@ TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState) {
|
||||
});
|
||||
};
|
||||
|
||||
exec(ctx.createSerializer(), t1);
|
||||
exec(ctx.createDeserializer(), r1);
|
||||
ctx.createSerializer().object(t1, exec);
|
||||
ctx.createDeserializer().object(r1, exec);
|
||||
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
std::variant<std::monostate> t2{};
|
||||
|
||||
@@ -206,4 +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));
|
||||
}
|
||||
|
||||
@@ -69,12 +69,12 @@ void serialize(S& s, X& o)
|
||||
template <typename S>
|
||||
void serialize(S& s, Y& o)
|
||||
{
|
||||
auto writeInt = [&s]( int& v) { s.template value<sizeof(v)>(v); };
|
||||
auto writeInt = [](S& s, int& v) { s.template value<sizeof(v)>(v); };
|
||||
s.template text<1>(o.s, 10000);
|
||||
s.template value<sizeof(o.y)>(o.y);
|
||||
s.container(o.arr, writeInt);
|
||||
s.container(o.carr, writeInt);
|
||||
s.container(o.vx, 10000, [&s](X& v) { s.object(v); });
|
||||
s.container(o.vx, 10000, [](S& s, X& v) { s.object(v); });
|
||||
}
|
||||
|
||||
|
||||
@@ -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,9 +28,11 @@ using testing::Eq;
|
||||
|
||||
bool SerializeDeserializeContainerSize(SerializationContext& ctx, const size_t size) {
|
||||
std::vector<char> t1(size);
|
||||
ctx.createSerializer().container(t1, size+1, []( char& ){});
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.container(t1, size+1, [](decltype(ser)& , char& ){});
|
||||
t1.clear();
|
||||
ctx.createDeserializer().container(t1, size+1, []( char& ){});
|
||||
auto des = ctx.createDeserializer();
|
||||
des.container(t1, size+1, [](decltype(des)&, char& ){});
|
||||
return t1.size() == size;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,10 +86,6 @@ void serialize(S&s, MyStruct2& o) {
|
||||
s.object(o.s1);
|
||||
}
|
||||
|
||||
struct SessionsEnabledConfig: public bitsery::DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
using Buffer = std::vector<char>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
@@ -100,34 +96,59 @@ using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
|
||||
template <typename Config, typename Context>
|
||||
class BasicSerializationContext {
|
||||
public:
|
||||
using TWriter = bitsery::AdapterWriter<OutputAdapter, Config>;
|
||||
using TReader = bitsery::AdapterReader<InputAdapter, Config>;
|
||||
using TSerializer = bitsery::BasicSerializer<TWriter, Context>;
|
||||
using TDeserializer = bitsery::BasicDeserializer<TReader, Context>;
|
||||
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 TSerializerBPEnabled = typename TSerializer::BPEnabledType;
|
||||
using TDeserializerBPEnabled = typename TDeserializer::BPEnabledType;
|
||||
|
||||
Buffer buf{};
|
||||
std::unique_ptr<TSerializer> ser{};
|
||||
std::unique_ptr<bitsery::BasicDeserializer<TReader, Context>> des{};
|
||||
TWriter* bw{};
|
||||
TReader* br{};
|
||||
std::unique_ptr<TWriter> bw{};
|
||||
std::unique_ptr<TReader> br{};
|
||||
|
||||
TSerializer& createSerializer(Context* ctx = nullptr) {
|
||||
if (!ser) {
|
||||
ser = std::unique_ptr<TSerializer>(new TSerializer(OutputAdapter{buf}, ctx));
|
||||
bw = &bitsery::AdapterAccess::getWriter(*ser);
|
||||
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}});
|
||||
}
|
||||
return *ser;
|
||||
};
|
||||
return TSerializer{*bw};
|
||||
}
|
||||
|
||||
TDeserializer & createDeserializer(Context* ctx = nullptr) {
|
||||
bw->flush();
|
||||
if (!des) {
|
||||
des = std::unique_ptr<TDeserializer>(
|
||||
new TDeserializer(InputAdapter{buf.begin(), bw->writtenBytesCount()}, ctx));
|
||||
br = &bitsery::AdapterAccess::getReader(*des);
|
||||
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});
|
||||
}
|
||||
return *des;
|
||||
};
|
||||
return TSerializer{*bw};
|
||||
}
|
||||
|
||||
|
||||
template <typename T=Context, typename std::enable_if<std::is_void<T>::value>::type* = nullptr>
|
||||
TDeserializer createDeserializer() {
|
||||
size_t writtenBytes = 0;
|
||||
if (bw) {
|
||||
bw->flush();
|
||||
writtenBytes = bw->writtenBytesCount();
|
||||
}
|
||||
if (!br) {
|
||||
br = std::unique_ptr<TReader>(new TReader{InputAdapter{buf.begin(), writtenBytes}});
|
||||
}
|
||||
return TDeserializer{*br};
|
||||
}
|
||||
|
||||
template <typename T=Context>
|
||||
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 (!br) {
|
||||
br = std::unique_ptr<TReader>(new TReader{InputAdapter{buf.begin(), writtenBytes}, ctx});
|
||||
}
|
||||
return TDeserializer{*br};
|
||||
}
|
||||
|
||||
size_t getBufferSize() const {
|
||||
return bw->writtenBytesCount();
|
||||
|
||||
@@ -118,4 +118,12 @@ TEST(SerializeText, WhenCArrayNotNullterminatedThenAssert) {
|
||||
t1[CARR_LENGTH-1] = 'x';
|
||||
EXPECT_DEATH(ctx.createSerializer().text<2>(t1), "");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TEST(SerializeText, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidDataError) {
|
||||
SerializationContext ctx;
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user