diff --git a/CHANGELOG.md b/CHANGELOG.md index 9599b89..fa11b7b 100644 --- a/CHANGELOG.md +++ b/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 will never return nullptr +* if context is optional contextOrNull return null in case context is not defined +* context and contextOrNull 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) diff --git a/examples/bit_packing.cpp b/examples/bit_packing.cpp index 625d40a..8ac58a9 100644 --- a/examples/bit_packing.cpp +++ b/examples/bit_packing.cpp @@ -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 range{-1.0f,1.0f, 0.01f}; sbp.ext(vec3.x, range); sbp.ext(vec3.y, range); diff --git a/examples/composite_types.cpp b/examples/composite_types.cpp index 5b81672..f16fa19 100644 --- a/examples/composite_types.cpp +++ b/examples/composite_types.cpp @@ -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{}); }); }, diff --git a/examples/context_usage.cpp b/examples/context_usage.cpp index fba9ca1..0211cdb 100644 --- a/examples/context_usage.cpp +++ b/examples/context_usage.cpp @@ -34,15 +34,15 @@ namespace MyTypes { template 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(); - //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>(); - s.container(o.monsters, *maxMonsters, [&s, dmgRange] (Monster& m) { + //NOTE: if context is optional then you can call contextOrNull, and it will return null if T doesn't exists + auto maxMonsters = s.template context(); + auto& dmgRange = s.template context>(); + + 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 range{dmgRange->first, dmgRange->second}; + bitsery::ext::ValueRange 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; -using OutputAdapter = OutputBufferAdapter; -using InputAdapter = InputBufferAdapter; -//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(); -//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(); -using Context = std::tuple>; - //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>; + +//use fixed-size buffer +using Buffer = std::vector; +using namespace bitsery; +// define Writer and Reader types, +using Writer = AdapterWriter, DefaultConfig, Context>; +using Reader = AdapterReader, 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, Context> ser{buffer, &ctx}; + //create adapter writer with context + //context is passed by reference without taking ownership + Writer writer{buffer, ctx}; + //serialize data + BasicSerializer ser{writer}; ser.object(data); - - auto& w = AdapterAccess::getWriter(ser); - w.flush(); - auto writtenSize = w.writtenBytesCount(); + writer.flush(); MyTypes::GameState res{}; - BasicDeserializer , Context> des { InputAdapter{buffer.begin(), writtenSize}, &ctx}; + Reader reader {{buffer.begin(), writer.writtenBytesCount()}, ctx}; + BasicDeserializer des {reader }; des.object(res); - auto& r = AdapterAccess::getReader(des); - assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully()); + assert(reader.error() == ReaderError::NoError && reader.isCompletedSuccessfully()); } diff --git a/examples/file_stream.cpp b/examples/file_stream.cpp index 535eb97..6093b96 100644 --- a/examples/file_stream.cpp +++ b/examples/file_stream.cpp @@ -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; 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 ser{s}; + Writer writer{s}; + BasicSerializer ser{writer}; ser.object(data); //flush to writer - AdapterAccess::getWriter(ser).flush(); + writer.flush(); s.close(); //reopen for reading diff --git a/examples/forward_backward_compatibility.cpp b/examples/forward_backward_compatibility.cpp index 6282feb..a94af2b 100644 --- a/examples/forward_backward_compatibility.cpp +++ b/examples/forward_backward_compatibility.cpp @@ -25,7 +25,7 @@ namespace MyTypes { template 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 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; using OutputAdapter = OutputBufferAdapter; using InputAdapter = InputBufferAdapter; -//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> ser{OutputAdapter{buffer}}; - ser.object(data); - auto& w = AdapterAccess::getWriter(ser); - w.flush(); - auto writtenSize = w.writtenBytesCount(); - + auto writtenSize = quickSerialization(buffer, data); MyTypes::Monster res{}; //deserialize - BasicDeserializer> des{InputAdapter{buffer.begin(), writtenSize}}; - des.object(res); - auto& r = AdapterAccess::getReader(des); - assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully()); + auto state = quickDeserialization({buffer.begin(), writtenSize}, res); + + assert(state.first == ReaderError::NoError && state.second); } diff --git a/examples/inheritance.cpp b/examples/inheritance.cpp index 05cee02..0a7eac5 100644 --- a/examples/inheritance.cpp +++ b/examples/inheritance.cpp @@ -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 using bitsery::ext::BaseClass; @@ -32,7 +31,7 @@ struct Derive1:virtual Base {// virtually inherits from base }; template 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{}); 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; -}; //some helper types using Buffer = std::vector; -using Writer = AdapterWriter, ConfigWithContext>; -using Reader = AdapterReader, ConfigWithContext>; +using Writer = AdapterWriter, DefaultConfig, ext::InheritanceContext>; +using Reader = AdapterReader, DefaultConfig, ext::InheritanceContext>; int main() { @@ -103,15 +95,20 @@ int main() { Buffer buf{}; - BasicSerializer ser{OutputBufferAdapter{buf}, nullptr}; + ext::InheritanceContext ctx1; + Writer writer{buf, ctx1}; + BasicSerializer ser{writer}; ser.object(data); - auto writtenSize = AdapterAccess::getWriter(ser).writtenBytesCount(); + writer.flush(); + MultipleInheritance res{0}; - BasicDeserializer des{InputBufferAdapter{buf.begin(), writtenSize}}; + ext::InheritanceContext ctx2; + Reader reader{{buf.begin(), writer.writtenBytesCount()}, ctx2}; + BasicDeserializer 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 } diff --git a/examples/non_default_constructible.cpp b/examples/non_default_constructible.cpp index 73276dd..e29792d 100644 --- a/examples/non_default_constructible.cpp +++ b/examples/non_default_constructible.cpp @@ -34,8 +34,8 @@ using namespace bitsery; //some helper types using Buffer = std::vector; -using OutputAdapter = OutputBufferAdapter; -using InputAdapter = InputBufferAdapter; +using Writer = AdapterWriter, DefaultConfig>; +using Reader = AdapterReader, DefaultConfig>; int main() { @@ -48,17 +48,18 @@ int main() { //create buffer Buffer buffer{}; - //create and serialize container, and get written bytes count - Serializer ser{OutputAdapter{buffer}}; + //create writer and serialize container + Writer writer{buffer}; + BasicSerializer ser{writer}; ser.container(data, 10); - auto writtenBytes = AdapterAccess::getWriter(ser).writtenBytesCount(); + writer.flush(); - //create and deserialize container - Deserializer des{InputAdapter{buffer.begin(), writtenBytes}}; + //create reader and deserialize container + Reader reader{{buffer.begin(), writer.writtenBytesCount()}}; + BasicDeserializer 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); } diff --git a/examples/raw_pointers.cpp b/examples/raw_pointers.cpp index e4ca744..6509b87 100644 --- a/examples/raw_pointers.cpp +++ b/examples/raw_pointers.cpp @@ -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; using InputAdapter = InputBufferAdapter; //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 //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, ext::PointerLinkingContext>; -using MyDeserializer = BasicDeserializer, ext::PointerLinkingContext>; +using Writer = AdapterWriter, DefaultConfig, ext::PointerLinkingContext>; +using Reader = AdapterReader, 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 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 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()); } diff --git a/examples/smart_pointers_with_polymorphism.cpp b/examples/smart_pointers_with_polymorphism.cpp index 1dde62b..9c1be70 100644 --- a/examples/smart_pointers_with_polymorphism.cpp +++ b/examples/smart_pointers_with_polymorphism.cpp @@ -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 &item) { + s.container(o.sharedList, 100, [](S& s, std::shared_ptr &item) { s.ext(item, StdSmartPtr{}); }); } @@ -197,8 +197,8 @@ using TContext = std::tuple, TContext>; -using MyDeserializer = BasicDeserializer, TContext>; +using Writer = AdapterWriter, DefaultConfig, TContext>; +using Reader = AdapterReader, 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(MyPolymorphicClassesForRegistering{}); - //serialize our data - MySerializer ser{OutputAdapter{buffer}, &ctx}; + std::get<1>(ctx).registerBasesList>(MyPolymorphicClassesForRegistering{}); + //create writer and serialize + Writer writer{buffer, ctx}; + BasicSerializer 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(MyPolymorphicClassesForRegistering{}); + std::get<1>(ctx).registerBasesList>(MyPolymorphicClassesForRegistering{}); //serialize our data - MyDeserializer des{InputAdapter{buffer.begin(), writtenSize}, &ctx}; + Reader reader {{buffer.begin(), writtenSize}, ctx}; + BasicDeserializer 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, diff --git a/include/bitsery/adapter/buffer.h b/include/bitsery/adapter/buffer.h index e39e457..316ce6c 100644 --- a/include/bitsery/adapter/buffer.h +++ b/include/bitsery/adapter/buffer.h @@ -28,7 +28,7 @@ namespace bitsery { - //base class that stores container iterators, and is required for session support (for reading sessions data) + template class BufferIterators { static constexpr bool isConstBuffer = std::is_const::value; @@ -42,12 +42,12 @@ namespace bitsery { static_assert(details::IsDefined::value, "Please define BufferAdapterTraits or include from "); 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(begin, endIt) { + : BufferIterators(begin, endIt), + _endReadPos{endIt} { + } InputBufferAdapter(TIterator begin, size_t size) - : BufferIterators(begin, std::next(begin, size)) { + : BufferIterators(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(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(std::distance(this->beginIt, this->posIt)); + } + + void currentReadEndPos(size_t pos) { + const auto buffSize = static_cast(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(std::distance(this->beginIt, _endReadPos)); + } + ReaderError error() const { - auto res = std::distance(this->endIt, this->posIt); - if (res > 0) { - auto err = static_cast(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(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 class UnsafeInputBufferAdapter : public BufferIterators { 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(std::distance(this->beginIt, this->posIt)); + } + + void currentReadEndPos(size_t) { + static_assert(std::is_void::value, "`currentReadEndPos(size_t)` is not supported with UnsafeInputBufferAdapter"); + } + + size_t currentReadEndPos() const { + return static_cast(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 @@ -169,12 +227,26 @@ namespace bitsery { writeInternal(data, size, TResizable{}); } + void currentWritePos(size_t pos) { + const auto currPos =static_cast(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 (std::distance(std::begin(*_buffer), _outIt)); + } + void flush() { //this function might be useful for stream adapters } size_t writtenBytesCount() const { - return static_cast(std::distance(std::begin(*_buffer), _outIt)); + const auto pos =static_cast(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(std::distance(begin, std::end(*_buffer))) >= pos) { + _outIt = std::next(begin, pos); + } else { + traits::BufferAdapterTraits::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(std::distance(begin, std::end(*_buffer))) >= pos); + _outIt = std::next(begin, pos); + } }; } diff --git a/include/bitsery/adapter/stream.h b/include/bitsery/adapter/stream.h index eaf09c5..1a9d2c4 100644 --- a/include/bitsery/adapter/stream.h +++ b/include/bitsery/adapter/stream.h @@ -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& istream) :_ios{std::addressof(istream)} {} void read(TValue* data, size_t size) { - if (static_cast(_ios->rdbuf()->sgetn( data , size )) != size) { + if (size - static_cast(_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::value, "setting read position is not supported with StreamAdapter"); } + + size_t currentReadPos() const { + static_assert(std::is_void::value, "setting read position is not supported with StreamAdapter"); + return {}; + } + + void currentReadEndPos(size_t ) { + static_assert(std::is_void::value, "setting read position is not supported with StreamAdapter"); + } + + size_t currentReadEndPos() const { + static_assert(std::is_void::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::max(); + } } private: std::basic_ios* _ios; + size_t _zeroIfNoErrors{}; + ReaderError _err = ReaderError::NoError; }; template 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& 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::value, "setting write position is not supported with StreamAdapter"); + } + + size_t currentWritePos() const { + static_assert(std::is_void::value, "setting write position is not supported with StreamAdapter"); + return {}; + } + void flush() { if (auto ostream = dynamic_cast*>(_ios)) ostream->flush(); @@ -111,7 +141,6 @@ namespace bitsery { static_assert(details::IsDefined::value, "Please define BufferAdapterTraits or include from to use as buffer for BasicBufferedOutputStreamAdapter"); static_assert(traits::ContainerTraits::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& ostream, size_t bufferSize = 256) @@ -168,6 +197,15 @@ namespace bitsery { } } + void currentWritePos(size_t ) { + static_assert(std::is_void::value, "setting write position is not supported with StreamAdapter"); + } + + size_t currentWritePos() const { + static_assert(std::is_void::value, "setting write position is not supported with StreamAdapter"); + return {}; + } + void flush() { auto begin = std::begin(_buf); _adapter.write(std::addressof(*begin), static_cast(std::distance(begin, _outIt))); @@ -204,7 +242,6 @@ namespace bitsery { class BasicIOStreamAdapter:public BasicInputStreamAdapter, public BasicOutputStreamAdapter { 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& iostream) diff --git a/include/bitsery/adapter_reader.h b/include/bitsery/adapter_reader.h index 8a2c9bf..848a594 100644 --- a/include/bitsery/adapter_reader.h +++ b/include/bitsery/adapter_reader.h @@ -23,7 +23,7 @@ #ifndef BITSERY_ADAPTER_READER_H #define BITSERY_ADAPTER_READER_H -#include "details/sessions.h" +#include "details/adapter_common.h" #include #include @@ -32,50 +32,29 @@ namespace bitsery { template class AdapterReaderBitPackingWrapper; - template - struct AdapterReader { - //this is required by deserializer + template + struct AdapterReader: public details::AdapterAndContext { + + using details::AdapterAndContext::AdapterAndContext; + static constexpr bool BitPackingEnabled = false; - using TConfig = Config; - using TValue = typename InputAdapter::TValue; - - static_assert(details::IsDefined::value, "Please define adapter traits or include from "); - - 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 - void readBytes(T &v) { + void readBytes(T& v) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); directRead(&v, 1); } template - void readBuffer(T *buf, size_t count) { + void readBuffer(T* buf, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); directRead(buf, count); } template - void readBits(T &, size_t ) { + void readBits(T&, size_t) { static_assert(std::is_void::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::TValue; private: - friend class AdapterReaderBitPackingWrapper>; - - InputAdapter _inputAdapter; - typename std::conditional>, - session::DisabledSessionsReader>>::type - _session; template void directRead(T *v, size_t count) { + static_assert(!std::is_const::value, ""); - _inputAdapter.read(reinterpret_cast(v), sizeof(T) * count); + this->_adapter.read(reinterpret_cast(v), sizeof(T) * count); //swap each byte if necessary _swapDataBits(v, count, std::integral_constant{}); @@ -142,25 +116,12 @@ namespace bitsery { }; template - class AdapterReaderBitPackingWrapper { + class AdapterReaderBitPackingWrapper: public details::AdapterAndContextWrapper { public: - //this is required by deserializer + + using details::AdapterAndContextWrapper::AdapterAndContextWrapper; + static constexpr bool BitPackingEnabled = true; - using TConfig = typename TReader::TConfig; - //make TValue unsigned for bitpacking - using UnsignedValue = typename std::make_unsigned::type; - using ScratchType = typename details::ScratchType::type; - static_assert(details::IsDefined::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::type; if (!m_scratchBits) - _reader.template readBytes(v); + this->_wrapped.template readBytes(v); else readBits(reinterpret_cast(v), details::BitsSize::value); } @@ -183,7 +144,7 @@ namespace bitsery { static_assert(sizeof(T) == SIZE, ""); if (!m_scratchBits) { - _reader.template readBuffer(buf, count); + this->_wrapped.template readBuffer(buf, count); } else { using UT = typename std::make_unsigned::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::type; + using ScratchType = typename details::ScratchType::type; + ScratchType m_scratch{}; size_t m_scratchBits{}; @@ -243,7 +213,7 @@ namespace bitsery { auto bits = (std::min)(bitsLeft, details::BitsSize::value); if (m_scratchBits < bits) { UnsignedValue tmp; - _reader.template readBytes(tmp); + this->_wrapped.template readBytes(tmp); m_scratch |= static_cast(tmp) << m_scratchBits; m_scratchBits += details::BitsSize::value; } diff --git a/include/bitsery/adapter_writer.h b/include/bitsery/adapter_writer.h index 94854c8..6b8247b 100644 --- a/include/bitsery/adapter_writer.h +++ b/include/bitsery/adapter_writer.h @@ -23,75 +23,109 @@ #ifndef BITSERY_ADAPTER_WRITER_H #define BITSERY_ADAPTER_WRITER_H -#include "details/sessions.h" +#include "details/adapter_common.h" #include #include namespace bitsery { - template - struct BasicMeasureSize { - //measure class is bit-packing enabled, no need to create wrapper for it - static constexpr bool BitPackingEnabled = true; + template + class BasicMeasureSize { + struct NoExternalContext{}; + public: + + static constexpr bool BitPackingEnabled = true; using TConfig = Config; + using InternalContext = typename Config::InternalContext; + using ExternalContext = typename std::conditional::value, NoExternalContext, Context>::type; + using TValue = void; + + static_assert(details::IsSpecializationOf::value, + "Config::InternalContext must be std::tuple"); + + // take ownership of adapter + template ::value>::type* = nullptr> + explicit BasicMeasureSize() + :_internalContext{}, + _externalContext{} + { + } + + // get context by reference, do not take ownership of it + template ::value>::type* = nullptr> + explicit BasicMeasureSize(ExternalContext& ctx) + :_internalContext{}, + _externalContext{ctx} + { + } + + template void writeBytes(const T &) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bitsCount += details::BitsSize::value; - } - - template - void writeBits(const T &, size_t bitsCount) { - static_assert(std::is_integral() && std::is_unsigned(), ""); - assert(bitsCount <= details::BitsSize::value); - _bitsCount += bitsCount; + _currPosBits += details::BitsSize::value; } template void writeBuffer(const T *, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - _bitsCount += details::BitsSize::value * count; + _currPosBits += details::BitsSize::value * count; + } + + template + void writeBits(const T &, size_t bitsCount) { + static_assert(std::is_integral() && std::is_unsigned(), ""); + assert(bitsCount <= details::BitsSize::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::value, + ExternalContext,ExternalContext&>::type _externalContext; + + private: + size_t _prevLargestPos{}; + size_t _currPosBits{}; }; //helper type for default config @@ -100,28 +134,13 @@ namespace bitsery { template class AdapterWriterBitPackingWrapper; - template - struct AdapterWriter { - //this is required by serializer + template + struct AdapterWriter: public details::AdapterAndContext { + + using details::AdapterAndContext::AdapterAndContext; + static constexpr bool BitPackingEnabled = false; - using TConfig = Config; - using TValue = typename OutputAdapter::TValue; - - static_assert(details::IsDefined::value, "Please define adapter traits or include from "); - - 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::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>; + template void directWrite(T &&v, size_t count) { _directWriteSwapTag(std::forward(v), count, std::integral_constant(&res), sizeof(T)); + this->_adapter.write(reinterpret_cast(&res), sizeof(T)); }); } template void _directWriteSwapTag(const T *v, size_t count, std::false_type) { - _outputAdapter.write(reinterpret_cast(v), count * sizeof(T)); + this->_adapter.write(reinterpret_cast(v), count * sizeof(T)); } - OutputAdapter _outputAdapter; - typename std::conditional>, - session::DisabledSessionsWriter>>::type - _session{}; }; - //this class is used as wrapper for real AdapterWriter, it doesn't store writer itself just a reference template - class AdapterWriterBitPackingWrapper { + class AdapterWriterBitPackingWrapper: public details::AdapterAndContextWrapper { public: - //this is required by serializer + using details::AdapterAndContextWrapper::AdapterAndContextWrapper; + static constexpr bool BitPackingEnabled = true; - using TConfig = typename TWriter::TConfig; - - //make TValue unsigned for bit packing - using UnsignedType = typename std::make_unsigned::type; - using ScratchType = typename details::ScratchType::type; - static_assert(details::IsDefined::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(v); + this->_wrapped.template writeBytes(v); } else { using UT = typename std::make_unsigned::type; writeBitsInternal(reinterpret_cast(v), details::BitsSize::value); @@ -244,7 +240,7 @@ namespace bitsery { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); if (!_scratchBits) { - _writer.template writeBuffer(buf, count); + this->_wrapped.template writeBuffer(buf, count); } else { using UT = typename std::make_unsigned::type; //todo improve implementation @@ -268,27 +264,31 @@ namespace bitsery { writeBitsInternal(UnsignedType{}, (details::BitsSize::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::type; + using ScratchType = typename details::ScratchType::type; + static_assert(details::IsDefined::value, "Underlying adapter value type is not supported"); + + template void writeBitsInternal(const T &v, size_t size) { constexpr size_t valueSize = details::BitsSize::value; @@ -300,7 +300,7 @@ namespace bitsery { _scratchBits += bits; if (_scratchBits >= valueSize) { auto tmp = static_cast(_scratch & _MASK); - _writer.template writeBytes(tmp); + this->_wrapped.template writeBytes(tmp); _scratch >>= valueSize; _scratchBits -= valueSize; @@ -317,7 +317,7 @@ namespace bitsery { _scratchBits += size; if (_scratchBits >= details::BitsSize::value) { auto tmp = static_cast(_scratch & _MASK); - _writer.template writeBytes(tmp); + this->_wrapped.template writeBytes(tmp); _scratch >>= details::BitsSize::value; _scratchBits -= details::BitsSize::value; } @@ -327,8 +327,6 @@ namespace bitsery { const UnsignedType _MASK = (std::numeric_limits::max)(); ScratchType _scratch{}; size_t _scratchBits{}; - TWriter& _writer; - }; namespace details { diff --git a/include/bitsery/common.h b/include/bitsery/common.h index b41a9e9..4502c33 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -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. diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index 34961a3..7f767ce 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -31,50 +31,32 @@ namespace bitsery { - template + template 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>::type, TContext>; + TAdapterReader, + AdapterReaderBitPackingWrapper>::type>; - static_assert(details::IsSpecializationOf::value, - "Config::InternalContext must be std::tuple"); - - template - explicit BasicDeserializer(ReaderParam&& r, TContext* context = nullptr) - : _reader{std::forward(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 + T& context() { + return *details::getContext(_reader.context()); } template - T* context(){ - return details::getContext(_context, _internalContext); - } - - template - T* contextOrNull(){ - return details::getContextIfTypeExists(_context, _internalContext); + T* contextOrNull() { + return details::getContext(_reader.context()); } /* @@ -88,7 +70,7 @@ namespace bitsery { template void object(T &&obj, Fnc &&fnc) { - fnc(std::forward(obj)); + fnc(*this, std::forward(obj)); } /* @@ -104,7 +86,9 @@ namespace bitsery { template BasicDeserializer &operator()(T &&head, TArgs &&... tail) { + //serialize object details::ArchiveFunction::invoke(*this, std::forward(head)); + //expand other elements archive(std::forward(tail)...); return *this; } @@ -146,7 +130,7 @@ namespace bitsery { "extension doesn't support overload with `value`"); using ExtVType = typename traits::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; - extension.deserialize(*this, _reader, obj, [this](VType &v) { value(v);}); + extension.deserialize(*this, _reader, obj, [](BasicDeserializer& s, VType &v) { s.value(v);}); } template @@ -156,7 +140,7 @@ namespace bitsery { "extension doesn't support overload with `object`"); using ExtVType = typename traits::ExtensionTraits::TValue; using VType = typename std::conditional::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 @@ -337,14 +317,8 @@ namespace bitsery { void container8b(T &&obj) { container<8>(std::forward(obj)); } private: - friend AdapterAccess; - // this is required when creating bitpacking serializer, to access internal context - friend class BasicDeserializer::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 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 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 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 - using Deserializer = BasicDeserializer>; //helper function that set ups all the basic steps and after deserialziation returns status - template - std::pair quickDeserialization(Adapter adapter, T& value) { - Deserializer des{std::move(adapter)}; + template + std::pair quickDeserialization(InputAdapter adapter, T& value) { + AdapterReader reader{std::move(adapter)}; + BasicDeserializer> des{reader}; des.object(value); - auto& r = AdapterAccess::getReader(des); - return {r.error(), r.isCompletedSuccessfully()}; + return {reader.error(), reader.isCompletedSuccessfully()}; } } diff --git a/include/bitsery/details/adapter_common.h b/include/bitsery/details/adapter_common.h index f29b5d5..bb93952 100644 --- a/include/bitsery/details/adapter_common.h +++ b/include/bitsery/details/adapter_common.h @@ -39,6 +39,15 @@ namespace bitsery { namespace details { + template class Template> + struct IsSpecializationOf : std::false_type { + }; + + template class Template, typename... Args> + struct IsSpecializationOf, Template> : std::true_type { + }; + + template struct BitsSize:public std::integral_constant { 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 - static Iterator& posIteratorRef(TReader& r) { - return r.posIt; + template + class AdapterAndContext { + struct NoContext{}; + public: + using TConfig = Config; + using TContext = typename std::conditional::value, NoContext, Context>::type; + using TValue = typename Adapter::TValue; + + static_assert(details::IsDefined::value, "Please define adapter traits or include from "); + + // take ownership of adapter + template ::value>::type* = nullptr> + explicit AdapterAndContext(Adapter&& adapter) + : _adapter{std::move(adapter)}, + _context{} + { } - template - static Iterator& endIteratorRef(TReader& r) { - return r.endIt; + + // get context by reference, do not take ownership of it + template ::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::value, + TContext,TContext&>::type _context; }; + //this class is used as wrapper for real Adapter, it only stores reference to real thing + template + 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; + }; + + } } diff --git a/include/bitsery/details/adapter_utils.h b/include/bitsery/details/adapter_utils.h index 611c576..7f5f053 100644 --- a/include/bitsery/details/adapter_utils.h +++ b/include/bitsery/details/adapter_utils.h @@ -59,7 +59,7 @@ namespace bitsery { } } if (size > maxSize) { - r.setError(ReaderError::InvalidData); + r.error(ReaderError::InvalidData); size = {}; } } diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h index 8ca68c9..943611f 100644 --- a/include/bitsery/details/serialization_common.h +++ b/include/bitsery/details/serialization_common.h @@ -26,7 +26,7 @@ #include #include #include -#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 + struct FtorExtValue : public Ext { + template + void operator()(S& s, T& v) const { + s.template ext(v, static_cast(*this)); + } + }; + + template + struct FtorExtValue1b: FtorExtValue<1, Ext> {}; + template + struct FtorExtValue2b: FtorExtValue<2, Ext> {}; + template + struct FtorExtValue4b: FtorExtValue<4, Ext> {}; + template + struct FtorExtValue8b: FtorExtValue<8, Ext> {}; + + template + struct FtorExtObject : public Ext { + template + void operator()(S& s, T& v) const { + s.ext(v, static_cast(*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 { }; - - //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 - static typename Serializer::TWriter &getWriter(Serializer &s) { - return s._writer; - } - - template - 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 class Template> - struct IsSpecializationOf : std::false_type { - }; + template + struct FindIndex : std::integral_constant {}; - template class Template, typename... Args> - struct IsSpecializationOf, Template> : std::true_type { - }; + template + struct FindIndex : + std::conditional, FindIndex>::type + {}; - //helper types for better error messages - template - struct GetTypeIndex : std::integral_constant { - }; + template + struct GetConvertibleTypeIndexFromTuple; - //found it - template - struct GetTypeIndex : std::integral_constant { - }; + template + struct GetConvertibleTypeIndexFromTuple> : FindIndex<0, std::is_convertible...> {}; - //iteratates over types - template - struct GetTypeIndex : std::integral_constant::value> { - }; - template - struct HasType : std::integral_constant::value<(sizeof ... (TList)))> { - }; + template + struct IsExistsConvertibleTupleType; - template - struct HasContext : std::is_same { - }; + template + struct IsExistsConvertibleTupleType> : + std::integral_constant>::value != sizeof...(Us)> {}; - template - struct HasContext> : HasType { - }; /* - * 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 - TCast *getContextImpl(std::tuple *ctx, std::true_type) { - using TCastIndex = GetTypeIndex; - static_assert(HasType::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context."); - return std::addressof(std::get(*ctx)); + + template + TCast* getDirectlyIfExists(TContext& ctx, std::true_type) { + return &static_cast(ctx); } - template - TCast *getContextImpl(TContext *ctx, std::false_type) { - static_assert(std::is_convertible::value, "Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context."); - return static_cast(ctx); - } - - //get local ctx - template - TCast *chooseInternalOrExternalContext(TContext *, TInternalContext &internalCtx, std::true_type) { - return getContextImpl(&internalCtx, std::true_type{}); - } - - //get external ctx - template - TCast *chooseInternalOrExternalContext(TContext *ctx, TInternalContext &, std::false_type) { - return ctx - ? getContextImpl(ctx, IsSpecializationOf{}) - : nullptr; - } - - template - TCast *getContext(TContext *ctx, TInternalContext &internalCtx) { - return chooseInternalOrExternalContext(ctx, internalCtx, HasContext{}); - } - - /* - * get context, if type doesn't exists then do not static_assert but return null instead - */ - - template - TCast *getContextFromTypeIfExists(TContext *ctx, std::true_type) { - return static_cast(ctx); - } - - template - TCast *getContextFromTypeIfExists(TContext *, std::false_type) { + template + 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 - TCast *getContextImplIfExists(TContext *ctx, std::false_type) { - return getContextFromTypeIfExists(ctx, std::is_convertible{}); + + template + TCast* getFromTupleIfExists(std::tuple& ctx, std::true_type) { + using TupleIndex = GetConvertibleTypeIndexFromTuple>; + return &static_cast(std::get(ctx)); } - template - TCast *getContextFromTupleIfExists(TContext *ctx, std::true_type tmp) { - return getContextImpl(ctx, tmp); - } - - template - TCast *getContextFromTupleIfExists(TContext *, std::false_type) { + template + TCast* getFromTupleIfExists(std::tuple& , 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 - TCast *getContextImplIfExists(TContext *ctx, std::true_type) { - return getContextFromTupleIfExists(ctx, HasContext{}); + //non tuple context + template + TCast* getContext(TContext& ctx) { + return getDirectlyIfExists(ctx, std::is_convertible{}); } - //get local ctx - template - TCast *chooseInternalOrExternalContextIfExists(TContext *, TInternalContext &internalCtx, std::true_type) { - return getContextImplIfExists(&internalCtx, std::true_type{}); - } - - //get external ctx - template - TCast *chooseInternalOrExternalContextIfExists(TContext *ctx, TInternalContext &, std::false_type) { - return ctx - ? getContextImplIfExists(ctx, IsSpecializationOf{}) - : nullptr; - } - - template - TCast *getContextIfTypeExists(TContext *ctx, TInternalContext &internalCtx) { - return chooseInternalOrExternalContextIfExists(ctx, internalCtx, HasContext{}); + //tuple context + template + TCast* getContext(std::tuple& ctx) { + return getFromTupleIfExists(ctx, IsExistsConvertibleTupleType>{}); } } diff --git a/include/bitsery/details/sessions.h b/include/bitsery/details/sessions.h deleted file mode 100644 index a6f72b0..0000000 --- a/include/bitsery/details/sessions.h +++ /dev/null @@ -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 -#include -#include "adapter_common.h" - -namespace bitsery { - - namespace session { - /* - * writer/reader implementations that disable session support - */ - template - struct DisabledSessionsWriter { - void begin(TWriter& ) { - static_assert(std::is_void::value, "Sessions is disabled, enable it via configuration"); - } - void end(TWriter& ) { - static_assert(std::is_void::value, "Sessions is disabled, enable it via configuration"); - } - void flushSessions(TWriter& ) { - } - }; - - template - struct DisabledSessionsReader { - template - DisabledSessionsReader(TReader& , TBufferContext& ) { - } - - void begin() { - static_assert(std::is_void::value, "Sessions is disabled, enable it via configuration"); - } - - void end() { - static_assert(std::is_void::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 - 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(sessionsOffset)); - } - } - private: - std::vector _sessions{}; - std::stack _sessionIndex{}; - }; - - template - struct SessionsReader { - using TIterator = typename TReader::TIterator; - - template - SessionsReader(TReader& r, InputAdapter& adapter) - :_reader{r}, - _beginIt{details::SessionAccess::posIteratorRef(adapter)}, - _posItRef{details::SessionAccess::posIteratorRef(adapter)}, - _endItRef{details::SessionAccess::endIteratorRef(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(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 _sessions{}; - std::vector::iterator _nextSessionIt{}; - std::stack _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(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(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(sessionsOffset)); - _nextSessionIt = std::begin(_sessions);//set before first session; - return true; - } - }; - - } -} - -#endif //BITSERY_DETAILS_SESSIONS_H diff --git a/include/bitsery/ext/compact_value.h b/include/bitsery/ext/compact_value.h index eb7b861..6acd505 100644 --- a/include/bitsery/ext/compact_value.h +++ b/include/bitsery/ext/compact_value.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); } } diff --git a/include/bitsery/ext/entropy.h b/include/bitsery/ext/entropy.h index 5af0c32..3a5dd09 100644 --- a/include/bitsery/ext/entropy.h +++ b/include/bitsery/ext/entropy.h @@ -57,27 +57,27 @@ namespace bitsery { }; template - 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::size(_values) > 0); auto index = details::findEntropyIndex(obj, _values); s.ext(index, ext::ValueRange{0u, traits::ContainerTraits::size(_values)}); if (_alignBeforeData) - s.align(); + writer.align(); if (!index) - fnc(const_cast(obj)); + fnc(s, const_cast(obj)); } template - void deserialize(Des &d, Reader &, T &obj, Fnc &&fnc) const { + void deserialize(Des &d, Reader &reader, T &obj, Fnc &&fnc) const { assert(traits::ContainerTraits::size(_values) > 0); size_t index{}; d.ext(index, ext::ValueRange{0u, traits::ContainerTraits::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: diff --git a/include/bitsery/ext/growable.h b/include/bitsery/ext/growable.h index a244cf1..1ae794c 100644 --- a/include/bitsery/ext/growable.h +++ b/include/bitsery/ext/growable.h @@ -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 - void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const { - writer.beginSession(); - fnc(const_cast(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(0)); + + fnc(ser, const_cast(obj)); + + const auto endPos = writer.currentWritePos(); + writer.currentWritePos(startPos); + writer.template writeBytes<4>(static_cast(endPos - startPos)); + writer.currentWritePos(endPos); } template - 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); } }; } diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h index fd7a496..c881ca4 100644 --- a/include/bitsery/ext/inheritance.h +++ b/include/bitsery/ext/inheritance.h @@ -25,6 +25,7 @@ #include #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{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 _virtualBases{}; + std::unordered_set, std::equal_to, + pointer_utils::PolymorphicAllocatorWrapper + > _virtualBases; }; template @@ -78,10 +84,10 @@ namespace bitsery { auto& resObj = static_cast(obj); if (auto ctx = ser.template contextOrNull()) { ctx->beginBase(obj, resObj); - fnc(const_cast(resObj)); + fnc(ser, const_cast(resObj)); ctx->end(); } else { - fnc(const_cast(resObj)); + fnc(ser, const_cast(resObj)); } } @@ -90,10 +96,10 @@ namespace bitsery { auto& resObj = static_cast(obj); if (auto ctx = des.template contextOrNull()) { ctx->beginBase(obj, resObj); - fnc(resObj); + fnc(des, resObj); ctx->end(); } else { - fnc(resObj); + fnc(des, resObj); } } @@ -106,20 +112,20 @@ namespace bitsery { template void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const { - auto ctx = ser.template context(); + auto& ctx = ser.template context(); auto& resObj = static_cast(obj); - if (ctx->beginVirtualBase(obj, resObj)) - fnc(const_cast(resObj)); - ctx->end(); + if (ctx.beginVirtualBase(obj, resObj)) + fnc(ser, const_cast(resObj)); + ctx.end(); } template void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const { - auto ctx = des.template context(); + auto& ctx = des.template context(); auto& resObj = static_cast(obj); - if (ctx->beginVirtualBase(obj, resObj)) - fnc(resObj); - ctx->end(); + if (ctx.beginVirtualBase(obj, resObj)) + fnc(des, resObj); + ctx.end(); } }; diff --git a/include/bitsery/ext/pointer.h b/include/bitsery/ext/pointer.h index 4657bbd..a5dde19 100644 --- a/include/bitsery/ext/pointer.h +++ b/include/bitsery/ext/pointer.h @@ -49,21 +49,21 @@ namespace bitsery { return PointerOwnershipType::Owner; } - static void create(T& obj, PolymorphicAllocator& alloc, size_t typeId) { - obj = alloc.allocate(typeId); + static void create(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) { + obj = alloc.newObject(typeId); } - static void createPolymorphic(T& obj, PolymorphicAllocator& alloc, + static void createPolymorphic(T& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler) { obj = static_cast(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& 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 diff --git a/include/bitsery/ext/std_chrono.h b/include/bitsery/ext/std_chrono.h index 89ad068..2e51cf4 100644 --- a/include/bitsery/ext/std_chrono.h +++ b/include/bitsery/ext/std_chrono.h @@ -33,15 +33,15 @@ namespace bitsery { public: template - void serialize(Ser&, Writer&, const std::chrono::duration& obj, Fnc&& fnc) const { + void serialize(Ser& ser, Writer&, const std::chrono::duration& obj, Fnc&& fnc) const { auto res = obj.count(); - fnc(res); + fnc(ser, res); } template - void deserialize(Des&, Reader&, std::chrono::duration& obj, Fnc&& fnc) const { + void deserialize(Des& des, Reader&, std::chrono::duration& obj, Fnc&& fnc) const { T res{}; - fnc(res); + fnc(des, res); obj = std::chrono::duration{res}; } }; @@ -50,17 +50,17 @@ namespace bitsery { public: template - void serialize(Ser&, Writer&, const std::chrono::time_point>& obj, + void serialize(Ser& ser, Writer&, const std::chrono::time_point>& obj, Fnc&& fnc) const { auto res = obj.time_since_epoch().count(); - fnc(res); + fnc(ser, res); } template - void deserialize(Des&, Reader&, std::chrono::time_point>& obj, + void deserialize(Des& des, Reader&, std::chrono::time_point>& obj, Fnc&& fnc) const { T res{}; - fnc(res); + fnc(des, res); auto dur = std::chrono::duration{res}; obj = std::chrono::time_point>{dur}; } diff --git a/include/bitsery/ext/std_map.h b/include/bitsery/ext/std_map.h index 91eac90..77cd1df 100644 --- a/include/bitsery/ext/std_map.h +++ b/include/bitsery/ext/std_map.h @@ -38,7 +38,7 @@ namespace bitsery { constexpr explicit StdMap(size_t maxSize):_maxSize{maxSize} {} template - 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(v.first), const_cast(v.second)); + fnc(ser, const_cast(v.first), const_cast(v.second)); } template - 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()}; auto value{bitsery::Access::create()}; - fnc(key, value); + fnc(des, key, value); hint = obj.emplace_hint(hint, std::move(key), std::move(value)); } } diff --git a/include/bitsery/ext/std_optional.h b/include/bitsery/ext/std_optional.h index 450ae3f..7611040 100644 --- a/include/bitsery/ext/std_optional.h +++ b/include/bitsery/ext/std_optional.h @@ -41,23 +41,23 @@ namespace bitsery { explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {} template - void serialize(Ser &ser, Writer &, const std::optional &obj, Fnc &&fnc) const { + void serialize(Ser &ser, Writer &writer, const std::optional &obj, Fnc &&fnc) const { ser.boolValue(static_cast(obj)); if (_alignBeforeData) - ser.align(); + writer.align(); if (obj) - fnc(const_cast(*obj)); + fnc(ser, const_cast(*obj)); } template - void deserialize(Des &des, Reader &, std::optional &obj, Fnc &&fnc) const { + void deserialize(Des &des, Reader &reader, std::optional &obj, Fnc &&fnc) const { bool exists{}; des.boolValue(exists); if (_alignBeforeData) - des.align(); + reader.align(); if (exists) { obj = ::bitsery::Access::create(); - fnc(*obj); + fnc(des, *obj); } else { obj = std::nullopt; } diff --git a/include/bitsery/ext/std_set.h b/include/bitsery/ext/std_set.h index 5349d53..b26b37d 100644 --- a/include/bitsery/ext/std_set.h +++ b/include/bitsery/ext/std_set.h @@ -38,18 +38,18 @@ namespace bitsery { constexpr explicit StdSet(size_t maxSize):_maxSize{maxSize} {} template - 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(v)); + fnc(ser, const_cast(v)); } template - 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()}; - fnc(key); + fnc(des, key); hint = obj.emplace_hint(hint, std::move(key)); } } diff --git a/include/bitsery/ext/std_smart_ptr.h b/include/bitsery/ext/std_smart_ptr.h index 0df5e37..c61ceca 100644 --- a/include/bitsery/ext/std_smart_ptr.h +++ b/include/bitsery/ext/std_smart_ptr.h @@ -70,52 +70,52 @@ namespace bitsery { } template - static void create(std::unique_ptr& obj, PolymorphicAllocator& alloc, + static void create(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) { - obj.reset(alloc.allocate(typeId)); + obj.reset(alloc.newObject(typeId)); } template - static void createPolymorphic(std::unique_ptr& obj, PolymorphicAllocator& alloc, + static void createPolymorphic(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler) { obj.reset(static_cast(handler->create(alloc))); } template - static void destroy(std::unique_ptr& obj, PolymorphicAllocator& alloc, size_t typeId) { + static void destroy(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) { uniquePtrDestroy(obj, alloc, typeId, std::is_same, T>{}); } template - static void destroyPolymorphic(std::unique_ptr& obj, PolymorphicAllocator& alloc, + static void destroyPolymorphic(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler) { uniquePtrDestroyPolymorphic(obj, alloc, handler, std::is_same, T>{}); } - static void destroy(std::shared_ptr& obj, PolymorphicAllocator&, size_t) { + static void destroy(std::shared_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t) { obj.reset(); } - static void destroyPolymorphic(std::shared_ptr& obj, PolymorphicAllocator&, + static void destroyPolymorphic(std::shared_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, const std::shared_ptr&) { obj.reset(); } - static void destroy(std::weak_ptr& obj, PolymorphicAllocator&, size_t) { + static void destroy(std::weak_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t) { obj.reset(); } - static void destroyPolymorphic(std::weak_ptr& obj, PolymorphicAllocator&, + static void destroyPolymorphic(std::weak_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, const std::shared_ptr&) { obj.reset(); } static std::unique_ptr createShared( - std::shared_ptr& obj, PolymorphicAllocator& alloc, size_t typeId) { + std::shared_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) { // capture deleter parameters by value - obj = std::shared_ptr(alloc.allocate(typeId), - [&alloc, typeId](TElement* data) { - alloc.deallocate(data, typeId); + obj = std::shared_ptr(alloc.newObject(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 createSharedPolymorphic( - std::shared_ptr& obj, PolymorphicAllocator& alloc, + std::shared_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler) { // capture deleter parameters by value obj = std::shared_ptr(static_cast(handler->create(alloc)), @@ -136,10 +136,10 @@ namespace bitsery { } static std::unique_ptr createShared( - std::weak_ptr& obj, PolymorphicAllocator& alloc, size_t typeId) { - auto res = std::shared_ptr(alloc.allocate(typeId), + std::weak_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId) { + auto res = std::shared_ptr(alloc.newObject(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 createSharedPolymorphic( - std::weak_ptr& obj, PolymorphicAllocator& alloc, + std::weak_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler) { auto res = std::shared_ptr(static_cast(handler->create(alloc)), [alloc, handler](TElement* data) { @@ -177,15 +177,15 @@ namespace bitsery { private: template static void - uniquePtrDestroy(std::unique_ptr& obj, PolymorphicAllocator& alloc, size_t typeId, + uniquePtrDestroy(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, size_t typeId, std::true_type) { auto ptr = obj.release(); - alloc.deallocate(ptr, typeId); + alloc.deleteObject(ptr, typeId); } template static void - uniquePtrDestroyPolymorphic(std::unique_ptr& obj, PolymorphicAllocator& alloc, + uniquePtrDestroyPolymorphic(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId& alloc, const std::shared_ptr& handler, std::true_type) { auto ptr = obj.release(); handler->destroy(alloc, ptr); @@ -193,14 +193,14 @@ namespace bitsery { template static void - uniquePtrDestroy(std::unique_ptr& obj, PolymorphicAllocator&, size_t, + uniquePtrDestroy(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, size_t, std::false_type) { obj.reset(); } template static void - uniquePtrDestroyPolymorphic(std::unique_ptr& obj, PolymorphicAllocator&, + uniquePtrDestroyPolymorphic(std::unique_ptr& obj, pointer_utils::PolymorphicAllocatorWithTypeId&, const std::shared_ptr&, std::false_type) { obj.reset(); } diff --git a/include/bitsery/ext/utils/memory_allocator.h b/include/bitsery/ext/utils/memory_allocator.h index 43869f2..727ad9d 100644 --- a/include/bitsery/ext/utils/memory_allocator.h +++ b/include/bitsery/ext/utils/memory_allocator.h @@ -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 - T* allocate(size_t typeId) const { - constexpr auto bytes = sizeof(T); - constexpr auto alignment = std::alignment_of::value; - void* ptr = _resource - ? _resource->allocate(bytes, alignment, typeId) - : MemResourceNewDelete{}.allocate(bytes, alignment, typeId); - return ::bitsery::Access::create(ptr); - } + explicit constexpr PolymorphicAllocatorWithTypeId(MemResourceBase* memResource = nullptr) + :_resource{memResource} {} - template - void deallocate(T* ptr, size_t typeId) const { - constexpr auto bytes = sizeof(T); - constexpr auto alignment = std::alignment_of::value; - ptr->~T(); - _resource - ? _resource->deallocate(ptr, bytes, alignment, typeId) - : MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId); - } + template + T* allocate(size_t n, size_t typeId) const { + const auto bytes = sizeof(T) * n; + constexpr auto alignment = std::alignment_of::value; + void* ptr = _resource + ? _resource->allocate(bytes, alignment, typeId) + : ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId); + return static_cast(ptr); + } - void setMemResource(MemResourceBase* resource) { - _resource = resource; - } + template + void deallocate(T* ptr, size_t n, size_t typeId) const noexcept { + const auto bytes = sizeof(T) * n; + constexpr auto alignment = std::alignment_of::value; + _resource + ? _resource->deallocate(ptr, bytes, alignment, typeId) + : ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId); + } - MemResourceBase* getMemResource() const { - return _resource; - } + template + T* newObject(size_t typeId) const { + auto ptr = allocate(1, typeId); + return ::bitsery::Access::create(ptr); + } - private: - MemResourceBase* _resource{nullptr}; - }; + template + 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 + struct PolymorphicAllocatorWrapper final { + using value_type = T; + + explicit constexpr PolymorphicAllocatorWrapper(MemResourceBase* memResource) + :_alloc{memResource} {} + explicit constexpr PolymorphicAllocatorWrapper(PolymorphicAllocatorWithTypeId alloc) : _alloc{alloc} {} + + template + friend class PolymorphicAllocatorWrapper; + + template + constexpr explicit PolymorphicAllocatorWrapper(const PolymorphicAllocatorWrapper& other) noexcept + :_alloc{other._alloc} { + } + + T* allocate(std::size_t n) { + return _alloc.allocate(n, 0); + } + + void deallocate(T* p, std::size_t n) noexcept { + return _alloc.deallocate(p, n, 0); + } + + template + friend bool operator==(const PolymorphicAllocatorWrapper& lhs, + const PolymorphicAllocatorWrapper& rhs) noexcept { + return lhs._alloc == rhs._alloc; + } + + template + friend bool operator!=(const PolymorphicAllocatorWrapper& lhs, + const PolymorphicAllocatorWrapper& rhs) noexcept { + return !(lhs == rhs); + } + + private: + PolymorphicAllocatorWithTypeId _alloc; + }; + + } } + } #endif //BITSERY_EXT_MEMORY_ALLOCATOR_H diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h index ec93e6f..e31ba73 100644 --- a/include/bitsery/ext/utils/pointer_utils.h +++ b/include/bitsery/ext/utils/pointer_utils.h @@ -213,7 +213,12 @@ namespace bitsery { }); } + PolymorphicAllocatorWithTypeId& getAllocator() { + return _polyAlloc; + } + private: + PolymorphicAllocatorWithTypeId _polyAlloc{}; std::unordered_map _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::getPtr(const_cast(obj)); if (ptr) { - auto ctx = ser.template context(); - assert(ctx != nullptr); - auto& ptrInfo = ctx->getInfoByPtr(getBasePtr(ptr), TPtrManager::getOwnership()); + auto& ctx = ser.template context(); + auto& ptrInfo = ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager::getOwnership()); details::writeSize(w, ptrInfo.id); if (TPtrManager::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::max()); - auto ctx = des.template context(); - assert(ctx != nullptr); + auto& ctx = des.template context(); + auto& alloc = ctx.getAllocator(); if (id) { - auto& ptrInfo = ctx->getInfoById(id, TPtrManager::getOwnership()); - deserializeImpl(*ctx, ptrInfo, des, obj, std::forward(fnc), r, IsPolymorphic{}, + auto& ptrInfo = ctx.getInfoById(id, TPtrManager::getOwnership()); + deserializeImpl(alloc, ptrInfo, des, obj, std::forward(fnc), r, IsPolymorphic{}, OwnershipType::getOwnership()>{}); } else { if (_ptrType == PointerType::Nullable) { if (auto ptr = TPtrManager::getPtr(obj)) { - auto prevMemResource = ctx->getMemResource(); - if (_resource) ctx->setMemResource(_resource); - destroyPtr(*ctx, des, obj, IsPolymorphic{}); - ctx->setMemResource(prevMemResource); + auto prevMemResource = alloc.getMemResource(); + if (_resource) alloc.setMemResource(_resource); + destroyPtr(alloc, des, obj, IsPolymorphic{}); + alloc.setMemResource(prevMemResource); }; } else - r.setError(ReaderError::InvalidPointer); + r.error(ReaderError::InvalidPointer); } } private: template - 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>(); auto ptr = TPtrManager::getPtr(obj); - TPtrManager::destroyPolymorphic(obj, plc, ctx->getPolymorphicHandler(*ptr)); + TPtrManager::destroyPolymorphic(obj, alloc, ctx.getPolymorphicHandler(*ptr)); } template - void destroyPtr(PointerLinkingContext& plc, Des&, TObj& obj, + void destroyPtr(PolymorphicAllocatorWithTypeId& alloc, Des&, TObj& obj, std::false_type /*polymorphic*/) const { - TPtrManager::destroy(obj, plc, RTTI::template get::TElement>()); + TPtrManager::destroy(obj, alloc, RTTI::template get::TElement>()); } @@ -327,74 +330,74 @@ namespace bitsery { template void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, Writer& w, std::true_type) const { const auto& ctx = ser.template context>(); - ctx->serialize(ser, w, *ptr); + ctx.serialize(ser, w, *ptr); } template - 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 - 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) const { const auto& ctx = des.template context>(); - auto prevMemResource = plc.getMemResource(); - ctx->deserialize(des, r, TPtrManager::getPtr(obj), - [&obj, &plc, this, prevMemResource]( + auto prevMemResource = alloc.getMemResource(); + ctx.deserialize(des, r, TPtrManager::getPtr(obj), + [&obj, &alloc, this, prevMemResource]( const std::shared_ptr& handler) { - if (_resource) plc.setMemResource(_resource); - TPtrManager::createPolymorphic(obj, plc, handler); - if (!_resourcePropagate) plc.setMemResource(prevMemResource); + if (_resource) alloc.setMemResource(_resource); + TPtrManager::createPolymorphic(obj, alloc, handler); + if (!_resourcePropagate) alloc.setMemResource(prevMemResource); return TPtrManager::getPtr(obj); }, - [&obj, &plc, this](const std::shared_ptr& handler) { - if (_resource) plc.setMemResource(_resource); - TPtrManager::destroyPolymorphic(obj, plc, handler); + [&obj, &alloc, this](const std::shared_ptr& handler) { + if (_resource) alloc.setMemResource(_resource); + TPtrManager::destroyPolymorphic(obj, alloc, handler); }); - plc.setMemResource(prevMemResource); + alloc.setMemResource(prevMemResource); ptrInfo.processOwner(TPtrManager::getPtr(obj)); } template - 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) const { auto ptr = TPtrManager::getPtr(obj); if (ptr) { - fnc(*ptr); + fnc(des, *ptr); } else { - auto prevMemResource = plc.getMemResource(); - if (_resource) plc.setMemResource(_resource); - TPtrManager::create(obj, plc, RTTI::template get::TElement>()); - if (!_resourcePropagate) plc.setMemResource(prevMemResource); + auto prevMemResource = alloc.getMemResource(); + if (_resource) alloc.setMemResource(_resource); + TPtrManager::create(obj, alloc, RTTI::template get::TElement>()); + if (!_resourcePropagate) alloc.setMemResource(prevMemResource); ptr = TPtrManager::getPtr(obj); - fnc(*ptr); - plc.setMemResource(prevMemResource); + fnc(des, *ptr); + alloc.setMemResource(prevMemResource); } ptrInfo.processOwner(ptr); } template - 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) const { auto& sharedState = ptrInfo.sharedState; if (!sharedState) { const auto& ctx = des.template context>(); - auto prevMemResource = plc.getMemResource(); - ctx->deserialize(des, r, TPtrManager::getPtr(obj), - [&obj, &plc, &sharedState, this, prevMemResource]( + auto prevMemResource = alloc.getMemResource(); + ctx.deserialize(des, r, TPtrManager::getPtr(obj), + [&obj, &alloc, &sharedState, this, prevMemResource]( const std::shared_ptr& handler) { - if (_resource) plc.setMemResource(_resource); - sharedState = TPtrManager::createSharedPolymorphic(obj, plc, handler); - if (!_resourcePropagate) plc.setMemResource(prevMemResource); + if (_resource) alloc.setMemResource(_resource); + sharedState = TPtrManager::createSharedPolymorphic(obj, alloc, handler); + if (!_resourcePropagate) alloc.setMemResource(prevMemResource); return TPtrManager::getPtr(obj); }, - [&obj, &plc, this](const std::shared_ptr& handler) { - if (_resource) plc.setMemResource(_resource); - TPtrManager::destroyPolymorphic(obj, plc, handler); + [&obj, &alloc, this](const std::shared_ptr& handler) { + if (_resource) alloc.setMemResource(_resource); + TPtrManager::destroyPolymorphic(obj, alloc, handler); }); - plc.setMemResource(prevMemResource); + alloc.setMemResource(prevMemResource); if (!sharedState) sharedState = TPtrManager::getSharedState(obj); } @@ -403,38 +406,38 @@ namespace bitsery { } template - 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) const { auto& sharedState = ptrInfo.sharedState; if (!sharedState) { auto ptr = TPtrManager::getPtr(obj); - auto prevMemResource = plc.getMemResource(); + auto prevMemResource = alloc.getMemResource(); if (ptr) { sharedState = TPtrManager::getSharedState(obj); } else { - if (_resource) plc.setMemResource(_resource); - sharedState = TPtrManager::createShared(obj, plc, + if (_resource) alloc.setMemResource(_resource); + sharedState = TPtrManager::createShared(obj, alloc, RTTI::template get::TElement>()); - if (!_resourcePropagate) plc.setMemResource(prevMemResource); + if (!_resourcePropagate) alloc.setMemResource(prevMemResource); ptr = TPtrManager::getPtr(obj); } - fnc(*ptr); - plc.setMemResource(prevMemResource); + fnc(des, *ptr); + alloc.setMemResource(prevMemResource); } TPtrManager::loadFromSharedState(sharedState.get(), obj); ptrInfo.processOwner(TPtrManager::getPtr(obj)); } template - 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) const { - deserializeImpl(plc, ptrInfo, des, obj, fnc, r, polymorph, + deserializeImpl(alloc, ptrInfo, des, obj, fnc, r, polymorph, OwnershipType{}); } template - void deserializeImpl(PointerLinkingContext&, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&, + void deserializeImpl(PolymorphicAllocatorWithTypeId&, PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&, Reader&, isPolymorphic, OwnershipType) const { ptrInfo.processObserver(reinterpret_cast(TPtrManager::getPtrRef(obj))); } diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h index a8b42cb..3b55267 100644 --- a/include/bitsery/ext/utils/polymorphism_utils.h +++ b/include/bitsery/ext/utils/polymorphism_utils.h @@ -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(RTTI::template get())); + void* create(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc) const final { + return toBase(alloc.newObject(RTTI::template get())); } - void destroy(const PolymorphicAllocator& alloc, void* ptr) const final { - alloc.deallocate(fromBase(ptr), RTTI::template get()); + void destroy(const pointer_utils::PolymorphicAllocatorWithTypeId& alloc, void* ptr) const final { + alloc.deleteObject(fromBase(ptr), RTTI::template get()); } void process(void* ser, void* obj) const final { @@ -165,17 +165,6 @@ namespace bitsery { _baseToDerivedArray.clear(); } - template class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn> - [[deprecated("de/serializer instance is not required")]] void - registerBasesList(const TSerializer& s, PolymorphicClassesList) { - add(); - registerBasesList(s, PolymorphicClassesList{}); - } - - template 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 - 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(), RTTI::template get(obj)}; auto it = _baseToDerivedMap.find(key); @@ -218,7 +207,7 @@ namespace bitsery { template 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::max()); @@ -240,7 +229,7 @@ namespace bitsery { } handler->process(&des, obj); } else - reader.setError(ReaderError::InvalidPointer); + reader.error(ReaderError::InvalidPointer); } template diff --git a/include/bitsery/ext/value_range.h b/include/bitsery/ext/value_range.h index 251e596..d751c0b 100644 --- a/include/bitsery/ext/value_range.h +++ b/include/bitsery/ext/value_range.h @@ -179,7 +179,7 @@ namespace bitsery { reader.readBits(reinterpret_cast &>(v), _range.bitsRequired); details::setRangeValue(v, _range); if (!details::isRangeValid(v, _range)) { - reader.setError(ReaderError::InvalidData); + reader.error(ReaderError::InvalidData); v = _range.min; } } diff --git a/include/bitsery/flexible/map.h b/include/bitsery/flexible/map.h index beda904..61c27a0 100644 --- a/include/bitsery/flexible/map.h +++ b/include/bitsery/flexible/map.h @@ -31,7 +31,7 @@ namespace bitsery { template void serialize(S &s, std::map &obj, size_t maxSize = std::numeric_limits::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 void serialize(S &s, std::multimap &obj, size_t maxSize = std::numeric_limits::max()) { s.ext(obj, ext::StdMap{maxSize}, - [&s](Key& key, T& value) { + [](S& s, Key& key, T& value) { s.object(key); s.object(value); }); diff --git a/include/bitsery/flexible/unordered_map.h b/include/bitsery/flexible/unordered_map.h index 117f0fb..da5ef2e 100644 --- a/include/bitsery/flexible/unordered_map.h +++ b/include/bitsery/flexible/unordered_map.h @@ -31,7 +31,7 @@ namespace bitsery { template void serialize(S &s, std::unordered_map &obj, size_t maxSize = std::numeric_limits::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 void serialize(S &s, std::unordered_multimap &obj, size_t maxSize = std::numeric_limits::max()) { s.ext(obj, ext::StdMap{maxSize}, - [&s](Key& key, T& value) { + [](S& s, Key& key, T& value) { s.object(key); s.object(value); }); diff --git a/include/bitsery/serializer.h b/include/bitsery/serializer.h index 3e001fa..503652d 100644 --- a/include/bitsery/serializer.h +++ b/include/bitsery/serializer.h @@ -30,50 +30,32 @@ namespace bitsery { - template + template 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>::type, TContext>; + TAdapterWriter, + AdapterWriterBitPackingWrapper>::type>; - static_assert(details::IsSpecializationOf::value, - "Config::InternalContext must be std::tuple"); - - template - explicit BasicSerializer(WriterParam&& w, TContext* context = nullptr) - : _writer{std::forward(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 - T* context() { - return details::getContext(_context, _internalContext); + T& context() { + return *details::getContext(_writer.context()); } template T* contextOrNull() { - return details::getContextIfTypeExists(_context, _internalContext); + return details::getContext(_writer.context()); } /* @@ -86,7 +68,7 @@ namespace bitsery { template void object(const T &obj, Fnc &&fnc) { - fnc(const_cast(obj)); + fnc(*this, const_cast(obj)); } /* @@ -144,7 +126,7 @@ namespace bitsery { "extension doesn't support overload with `value`"); using ExtVType = typename traits::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; - extension.serialize(*this, _writer, obj, [this](VType &v) { value(v); }); + extension.serialize(*this, _writer, obj, [](BasicSerializer& s, VType &v) { s.value(v); }); } template @@ -154,7 +136,7 @@ namespace bitsery { "extension doesn't support overload with `object`"); using ExtVType = typename traits::ExtensionTraits::TValue; using VType = typename std::conditional::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 @@ -333,15 +311,10 @@ namespace bitsery { template void container8b(T &&obj) { container<8>(std::forward(obj)); } + private: - friend AdapterAccess; - // this is required when creating bitpacking serializer, to access internal context - friend class BasicSerializer::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::type; for (; first != last; ++first) { - fnc(const_cast(*first)); + fnc(*this, const_cast(*first)); } } @@ -406,11 +379,9 @@ namespace bitsery { template 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 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 - using Serializer = BasicSerializer>; - //helper function that set ups all the basic steps and after serialziation returns serialized bytes count - template - size_t quickSerialization(Adapter adapter, const T& value) { - Serializer ser{std::move(adapter)}; + template + size_t quickSerialization(OutputAdapter adapter, const T& value) { + AdapterWriter writer{std::move(adapter)}; + BasicSerializer> ser{writer}; ser.object(value); - auto& w = AdapterAccess::getWriter(ser); - w.flush(); - return w.writtenBytesCount(); + writer.flush(); + return writer.writtenBytesCount(); } template size_t quickMeasureSize(const T& value) { - BasicSerializer ser{MeasureSize{}}; + MeasureSize writer{}; + BasicSerializer ser{writer}; ser.object(value); - auto& w = AdapterAccess::getWriter(ser); - w.flush(); - return w.writtenBytesCount(); + writer.flush(); + return writer.writtenBytesCount(); } } diff --git a/include/bitsery/traits/core/traits.h b/include/bitsery/traits/core/traits.h index 1f91474..1f56324 100644 --- a/include/bitsery/traits/core/traits.h +++ b/include/bitsery/traits/core/traits.h @@ -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::value, diff --git a/tests/adapter.cpp b/tests/adapter.cpp new file mode 100644 index 0000000..bc3847a --- /dev/null +++ b/tests/adapter.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +//some helper types +using Buffer = std::vector; +using OutputAdapter = bitsery::OutputBufferAdapter; +using InputAdapter = bitsery::InputBufferAdapter; +using Writer = bitsery::AdapterWriter; +using Reader = bitsery::AdapterReader; + +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(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; + using Adapter2 = bitsery::UnsafeInputBufferAdapter; + + bitsery::AdapterReader r1{Adapter1{buf.begin(), buf.end()}}; + bitsery::AdapterReader 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 class TAdapter> +struct BufferConfig { + using Data = std::vector; + using Adapter = TAdapter; + using Reader = bitsery::AdapterReader; + + Data data{}; + Reader createReader(const std::vector& buffer) { + data = buffer; + return Reader{Adapter{data.begin(), data.size()}}; + } +}; + +template +struct StreamConfig { + using Data = std::stringstream; + using Adapter = TAdapter; + using Reader = bitsery::AdapterReader; + + Data data{}; + Reader createReader(const std::vector& buffer) { + std::string str(buffer.begin(), buffer.end()); + data = std::stringstream{str}; + return Reader{Adapter{data}}; + } +}; + +template +class AdapterConfig : public testing::Test { +public: + + TAdapterWithData config{}; +}; + +using AdapterInputTypes = ::testing::Types< + BufferConfig, + BufferConfig, + StreamConfig +>; + +template +class InputAll: public AdapterConfig { +}; + +TYPED_TEST_CASE(InputAll, AdapterInputTypes); + + +using AdapterInputSafeOnlyTypes = ::testing::Types< + BufferConfig, + StreamConfig +>; + +template +class InputSafeOnly: public AdapterConfig { +}; + +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 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 +class OutputStreamBuffered : public testing::Test { +public: + using Buffer = T; + using Adapter = bitsery::BasicBufferedOutputStreamAdapter, Buffer>; + using Writer = bitsery::AdapterWriter; + + static constexpr size_t InternalBufferSize = 128; + + std::stringstream stream{}; + + Writer writer{{stream, 128}}; +}; + +using BufferedAdapterInternalBufferTypes = ::testing::Types< + std::vector, + std::array, + 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::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; +}; + +TEST(AdapterWriterMeasureSize, SupportsInternalAndExternalContexts) { + char extCtx{'A'}; + bitsery::BasicMeasureSize w{extCtx}; + EXPECT_THAT(w.externalContext(), Eq('A')); + std::tuple& tmp = w.internalContext(); +} diff --git a/tests/adapter_stream.cpp b/tests/adapter_stream.cpp deleted file mode 100644 index d465b12..0000000 --- a/tests/adapter_stream.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include - -//some helper types -using Stream = std::stringstream; -using OutputAdapter = bitsery::OutputStreamAdapter; -using InputAdapter = bitsery::InputStreamAdapter ; -using Writer = bitsery::AdapterWriter; -using Reader = bitsery::AdapterReader; - -static constexpr size_t InternalBufferSize = 128; -using BufferedAdapterInternalBuffer = std::array; -using OutputBufferedAdapter = bitsery::BasicBufferedOutputStreamAdapter, BufferedAdapterInternalBuffer>; -using BufferedWriter = bitsery::AdapterWriter; - -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 -class AdapterBufferedOutputStream : public testing::Test { -public: - using Buffer = T; - using Adapter = bitsery::BasicBufferedOutputStreamAdapter, Buffer>; - using Writer = bitsery::AdapterWriter; - - static constexpr size_t InternalBufferSize = 128; - - Stream stream{}; - - Writer writer{{stream, 128}}; -}; - -using BufferedAdapterInternalBufferTypes = ::testing::Types< - std::vector, - std::array, - 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::isResizable; - EXPECT_THAT(this->stream.str().empty(), ::testing::Ne(ShouldWriteToStream)); -} \ No newline at end of file diff --git a/tests/data_reading.cpp b/tests/data_reading.cpp deleted file mode 100644 index bc11a0c..0000000 --- a/tests/data_reading.cpp +++ /dev/null @@ -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 -#include "serialization_test_utils.h" -#include -#include - -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 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; - using Adapter2 = bitsery::UnsafeInputBufferAdapter; - - bitsery::AdapterReader r1{Adapter1{buf.begin(), buf.end()}}; - bitsery::AdapterReader 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)); -} diff --git a/tests/data_reading_errors.cpp b/tests/data_reading_errors.cpp deleted file mode 100644 index be866b8..0000000 --- a/tests/data_reading_errors.cpp +++ /dev/null @@ -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 -#include -#include "serialization_test_utils.h" - - -using testing::Eq; -using SessionsEnabledWriter = bitsery::AdapterWriter; -using SessionsEnabledReader = bitsery::AdapterReader; - -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 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)); -} diff --git a/tests/flexible_syntax.cpp b/tests/flexible_syntax.cpp index 8d91b7d..deb6db6 100644 --- a/tests/flexible_syntax.cpp +++ b/tests/flexible_syntax.cpp @@ -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(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 ctx; - ctx.createSerializer(&plctx1).archive(dataShared1, dataWeak1, dataUnique1); + ctx.createSerializer(plctx1).archive(dataShared1, dataWeak1, dataUnique1); std::shared_ptr resShared1{}; std::weak_ptr resWeak1{}; std::unique_ptr resUnique1{}; - ctx.createDeserializer(&plctx1).archive(resShared1, resWeak1, resUnique1); + ctx.createDeserializer(plctx1).archive(resShared1, resWeak1, resUnique1); //clear shared state from pointer linking context plctx1.clearSharedState(); diff --git a/tests/not_default_constructible.cpp b/tests/not_default_constructible.cpp index 8b0c257..ca09b40 100644 --- a/tests/not_default_constructible.cpp +++ b/tests/not_default_constructible.cpp @@ -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(bitsery::ext::PolymorphicClassesList{}); std::get<1>(desCtx).registerBasesList(bitsery::ext::PolymorphicClassesList{}); - ctx.createSerializer(&serCtx).object(data); - ctx.createDeserializer(&desCtx).object(res); + ctx.createSerializer(serCtx).object(data); + ctx.createDeserializer(desCtx).object(res); auto respp = dynamic_cast(res.pp); auto resup = dynamic_cast(res.up.get()); auto ressp = dynamic_cast(res.sp.get()); diff --git a/tests/serialization_bool.cpp b/tests/serialization_bool.cpp index 1c9c376..ec93bb6 100644 --- a/tests/serialization_bool.cpp +++ b/tests/serialization_bool.cpp @@ -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)); +} \ No newline at end of file diff --git a/tests/serialization_container.cpp b/tests/serialization_container.cpp index 78d2c85..5e5d915 100644 --- a/tests/serialization_container.cpp +++ b/tests/serialization_container.cpp @@ -64,6 +64,13 @@ std::list getFilledContainer>() { }; } +struct EmptyFtor { + template + 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(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(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 { TEST_P(SerializeContainer, SizeHasVariableLength) { SerializationContext ctx{}; - auto emptyFnc = [](uint8_t &) {}; std::vector src(GetParam()); std::vector res{}; - ctx.createSerializer().container(src, std::numeric_limits::max(), emptyFnc); - ctx.createDeserializer().container(res, std::numeric_limits::max(), emptyFnc); + ctx.createSerializer().container(src, std::numeric_limits::max(), EmptyFtor{}); + ctx.createDeserializer().container(res, std::numeric_limits::max(), EmptyFtor{}); EXPECT_THAT(res.size(), Eq(src.size())); EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size()))); diff --git a/tests/serialization_context.cpp b/tests/serialization_context.cpp index db92913..82294fa 100644 --- a/tests/serialization_context.cpp +++ b/tests/serialization_context.cpp @@ -26,136 +26,83 @@ using testing::Eq; -template -struct ConfigWithContext: bitsery::DefaultConfig { - using InternalContext = std::tuple; -}; - -template -using SerializerConfigWithContext = bitsery::BasicSerializer< - bitsery::AdapterWriter, ConfigWithContext>, Context>; - -template -using DeserializerConfigWithContext = bitsery::BasicDeserializer< - bitsery::AdapterReader, ConfigWithContext>, Context>; - -template -using MySerializer = bitsery::BasicSerializer; - -template -using MyDeserializer = bitsery::BasicDeserializer; +using bitsery::DefaultConfig; using SingleTypeContext = int; using MultipleTypesContext = std::tuple; -TEST(SerializationContext, WhenUsingContextThenReturnsUnderlyingPointerOrNull) { - Buffer buf{}; - MySerializer ser1{buf, nullptr}; - EXPECT_THAT(ser1.context(), ::testing::IsNull()); - - MySerializer ser2{buf, nullptr}; - EXPECT_THAT(ser2.context(), ::testing::IsNull()); - - SingleTypeContext sctx{}; - MyDeserializer des1{InputAdapter{buf.begin(), 1}, &sctx}; - EXPECT_THAT(des1.context(), Eq(&sctx)); - - MultipleTypesContext mctx{}; - MyDeserializer des2{InputAdapter{buf.begin(), 1}, &mctx}; - EXPECT_THAT(des2.context(), Eq(&mctx)); +TEST(SerializationContext, WhenContextIsNotTupleThenReturnThisContext) { + SingleTypeContext ctx{54}; + BasicSerializationContext c1; + auto ser1 = c1.createSerializer(ctx); + EXPECT_THAT(ser1.context(), Eq(ctx)); } -TEST(SerializationContext, WhenContextIsTupleThenContextCastOverloadCastsToIndividualTupleTypes) { - Buffer buf{}; - MySerializer ser1{buf, nullptr}; - EXPECT_THAT(ser1.context(), ::testing::IsNull()); - EXPECT_THAT(ser1.context(), ::testing::IsNull()); - EXPECT_THAT(ser1.context(), ::testing::IsNull()); +TEST(SerializationContext, WhenContextIsTupleThenReturnsTupleElements) { + + MultipleTypesContext ctx{5, 798.654, 'F'}; + BasicSerializationContext c1; + auto ser1 = c1.createSerializer(ctx); + + EXPECT_THAT(ser1.context(), std::get<0>(ctx)); + EXPECT_THAT(ser1.context(), std::get<1>(ctx)); + EXPECT_THAT(ser1.context(), std::get<2>(ctx)); } -TEST(SerializationContext, WhenContextIsNotTupleThenContextCastOverloadReturnSameType) { - Buffer buf{}; - SingleTypeContext ctx{}; - MySerializer ser1{buf, &ctx}; - EXPECT_THAT(ser1.context(), Eq(&ctx)); -} - -TEST(SerializationContext, SerializerDeserializerCanHaveInternalContextViaConfig) { - Buffer buf{}; - SerializerConfigWithContext ser{buf}; - EXPECT_THAT(ser.context(), ::testing::NotNull()); - EXPECT_THAT(*ser.context(), Eq(0)); - *ser.context() = 10; - EXPECT_THAT(*ser.context(), Eq(10)); - - DeserializerConfigWithContext des{InputAdapter{buf.begin(), 1}}; - EXPECT_THAT(des.context(), ::testing::NotNull()); - EXPECT_THAT(*des.context(), Eq(0)); - *des.context() = 10; - EXPECT_THAT(*des.context(), Eq(10)); - - //new instance has new context - SerializerConfigWithContext ser2{buf}; - EXPECT_THAT(ser2.context(), ::testing::NotNull()); - EXPECT_THAT(*ser2.context(), Eq(0)); -} - -TEST(SerializationContext, WhenInternalAndExternalContextIsTheSamePriorityGoesToInternalContext) { - Buffer buf{}; - int externalCtx = 5; - - SerializerConfigWithContext ser{buf, &externalCtx}; - EXPECT_THAT(ser.context(), ::testing::NotNull()); - EXPECT_THAT(*ser.context(), Eq(0)); - *ser.context() = 2; - - DeserializerConfigWithContext des{InputAdapter{buf.begin(), 1}, &externalCtx}; - EXPECT_THAT(des.context(), ::testing::NotNull()); - EXPECT_THAT(*des.context(), Eq(0)); - *des.context() = 3; - - EXPECT_THAT(externalCtx, Eq(5)); -} - -TEST(SerializationContext, ContextIfExistsReturnsNullWhenTypeDoesntExists) { - Buffer buf{}; - std::tuple extCtx1{}; - - SerializerConfigWithContext, float, int> ser{buf, &extCtx1}; - EXPECT_THAT(ser.contextOrNull(), ::testing::NotNull()); +TEST(SerializationContext, WhenContextDoesntExistsThenContextOrNullReturnsNull) { + SingleTypeContext ctx1= 32; + BasicSerializationContext c1; + auto ser = c1.createSerializer(ctx1); EXPECT_THAT(ser.contextOrNull(), ::testing::IsNull()); + EXPECT_THAT(ser.contextOrNull(), ::testing::NotNull()); + *ser.contextOrNull() = 2; + EXPECT_THAT(ctx1, Eq(2)); - double extCtx2{}; - DeserializerConfigWithContext des{InputAdapter{buf.begin(), 1}, &extCtx2}; - EXPECT_THAT(des.contextOrNull(), ::testing::NotNull()); - EXPECT_THAT(des.contextOrNull(), ::testing::IsNull()); + MultipleTypesContext ctx2{5, 798.654, 'F'}; + BasicSerializationContext c2; + auto des = c2.createDeserializer(ctx2); + EXPECT_THAT(des.contextOrNull(), ::testing::IsNull()); + EXPECT_THAT(des.contextOrNull(), ::testing::NotNull()); + EXPECT_THAT(*des.contextOrNull(), Eq('F')); + EXPECT_THAT(*des.contextOrNull(), Eq(5)); } -TEST(SerializationContext, WhenBitPackingIsEnabledThenInternalContextIsMovedToNewInstanceAndMovedBackAfterwards) { - Buffer buf{}; - using Ser = SerializerConfigWithContext; - using BPSer = typename Ser::BPEnabledType; - - using Des = DeserializerConfigWithContext; - using BPDes = typename Des::BPEnabledType; - - Ser ser{buf, nullptr}; - *ser.context() = 1; - EXPECT_THAT(*ser.context(), Eq(1)); - ser.enableBitPacking([](BPSer& s) { - EXPECT_THAT(*s.context(), Eq(1)); - *s.context() = 2; - }); - EXPECT_THAT(*ser.context(), Eq(2)); - - Des des{InputAdapter{buf.begin(), 1}, nullptr}; - *des.context() = 3; - EXPECT_THAT(*des.context(), Eq(3)); - des.enableBitPacking([](BPDes& d) { - EXPECT_THAT(*d.context(), Eq(3)); - *d.context() = 4; - }); - EXPECT_THAT(*des.context(), Eq(4)); +struct Base { int value{}; }; +struct Derived: Base{}; +TEST(SerializationContext, ContextWillTryToConvertIfTypeIsConvertible) { + Derived ctx1{}; + BasicSerializationContext c1; + auto ser = c1.createSerializer(ctx1); + EXPECT_THAT(ser.contextOrNull(), ::testing::NotNull()); + EXPECT_THAT(ser.contextOrNull(), ::testing::NotNull()); + ser.context(); + ser.context(); } + +TEST(SerializationContext, WhenMultipleConvertibleTypesExistsThenFirstMatchIsTaken) { + { + using CTX1 = std::tuple; + CTX1 ctx1{}; + std::get<0>(ctx1).value = 1; + std::get<2>(ctx1).value = 2; + BasicSerializationContext c1; + auto ser = c1.createSerializer(ctx1); + EXPECT_THAT(ser.context().value, Eq(std::get<2>(ctx1).value)); + EXPECT_THAT(ser.context().value, Eq(std::get<0>(ctx1).value)); + } + + { + using CTX2 = std::tuple; + CTX2 ctx2{}; + std::get<1>(ctx2).value = 1; + std::get<2>(ctx2).value = 2; + BasicSerializationContext c2; + auto des = c2.createSerializer(ctx2); + + EXPECT_THAT(des.context().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().value, Eq(std::get<1>(ctx2).value)); + } +} \ No newline at end of file diff --git a/tests/serialization_ext_compact_value.cpp b/tests/serialization_ext_compact_value.cpp index 8050081..3014bbf 100644 --- a/tests/serialization_ext_compact_value.cpp +++ b/tests/serialization_ext_compact_value.cpp @@ -228,13 +228,10 @@ TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) { SerializationContext ctx; auto data = getValue(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)); } diff --git a/tests/serialization_ext_entropy.cpp b/tests/serialization_ext_entropy.cpp index 1de8975..8638cdf 100644 --- a/tests/serialization_ext_entropy.cpp +++ b/tests/serialization_ext_entropy.cpp @@ -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>(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>{values}, [](MyStruct1& ) {}); + ser.ext(v, Entropy>{values}, [](BPSer& ,MyStruct1& ) {}); }); ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) { - des.ext(res, Entropy>{values}, []( MyStruct1& ) {}); + des.ext(res, Entropy>{values}, [](BPDes& , MyStruct1& ) {}); }); EXPECT_THAT(res, Eq(v)); diff --git a/tests/serialization_ext_growable.cpp b/tests/serialization_ext_growable.cpp index 3c334de..ef0429a 100644 --- a/tests/serialization_ext_growable.cpp +++ b/tests/serialization_ext_growable.cpp @@ -32,114 +32,73 @@ struct DataV1 { int32_t v1; }; +template +void serialize(S& s, DataV1& o) { + s.value4b(o.v1); +} + struct DataV2 { int32_t v1; int32_t v2; }; +template +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 + void serialize(S& s) { + s.value4b(v1); + s.value4b(v2); + s.value4b(v3); + } + }; - -TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) { - BasicSerializationContext 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 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_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 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 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{}(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{}(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 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{}(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{}(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 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{}(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{}(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 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{}(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{}(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{}(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{}(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 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{}(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 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 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{}(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 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)); } diff --git a/tests/serialization_ext_inheritance.cpp b/tests/serialization_ext_inheritance.cpp index 75a7416..9ba71ef 100644 --- a/tests/serialization_ext_inheritance.cpp +++ b/tests/serialization_ext_inheritance.cpp @@ -27,11 +27,7 @@ using bitsery::ext::BaseClass; using bitsery::ext::VirtualBaseClass; -struct ConfigWithInheritanceCtx:bitsery::DefaultConfig { - using InternalContext = std::tuple; -}; - -using SerContext = BasicSerializationContext; +using SerContext = BasicSerializationContext; using testing::Eq; @@ -68,7 +64,7 @@ struct Derive2NonVirtually:Base { template void serialize(S& s, Derive2NonVirtually& o) { //use lambda to serialize base - s.ext(o, BaseClass{}, [&s](Base& b) { + s.ext(o, BaseClass{}, [](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(res).x, Eq(static_cast(md).x)); EXPECT_THAT(static_cast(res).x, Eq(static_cast(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 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 void serialize(S& s, DerivedPrivateBase& o) { //use lambda for base serialization - s.ext(o, BaseClass{}, [&s](BasePrivateSerialize& b) { + s.ext(o, BaseClass{}, [](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)); } \ No newline at end of file diff --git a/tests/serialization_ext_pointer.cpp b/tests/serialization_ext_pointer.cpp index 565b52c..40101ac 100644 --- a/tests/serialization_ext_pointer.cpp +++ b/tests/serialization_ext_pointer.cpp @@ -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; ContextInTuple plctx2(0, PointerLinkingContext{}, 0.0f, 'a'); - BasicSerializationContext sctx2; - sctx2.createSerializer(&plctx2).ext(data, PointerObserver{}); - sctx2.createDeserializer(&plctx2).ext(data, PointerObserver{}); + BasicSerializationContext 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 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)); } diff --git a/tests/serialization_ext_pointer_polymorphic_types.cpp b/tests/serialization_ext_pointer_polymorphic_types.cpp index e109ad7..84607b4 100644 --- a/tests/serialization_ext_pointer_polymorphic_types.cpp +++ b/tests/serialization_ext_pointer_polymorphic_types.cpp @@ -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(bitsery::ext::PolymorphicClassesList{}); 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(bitsery::ext::PolymorphicClassesList{}); @@ -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(bitsery::ext::PolymorphicClassesList{}); diff --git a/tests/serialization_ext_pointer_with_allocator.cpp b/tests/serialization_ext_pointer_with_allocator.cpp index 9baa87e..f83f29b 100644 --- a/tests/serialization_ext_pointer_with_allocator.cpp +++ b/tests/serialization_ext_pointer_with_allocator.cpp @@ -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( @@ -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( @@ -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 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 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(new PolyPtrWithPolyPtrBase{}); data->ptr = std::unique_ptr(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(new PolyPtrWithPolyPtrBase{}); data->ptr = std::unique_ptr(new Derived1{5, 6}); diff --git a/tests/serialization_ext_std_map.cpp b/tests/serialization_ext_std_map.cpp index 6b8a230..b78ecbc 100644 --- a/tests/serialization_ext_std_map.cpp +++ b/tests/serialization_ext_std_map.cpp @@ -95,7 +95,7 @@ namespace bitsery { template void serialize(S& s, std::unordered_map& 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 void serialize(S& s, std::unordered_multimap& 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 void serialize(S& s, std::map& 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 void serialize(S& s, std::multimap& 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{-100,100}); diff --git a/tests/serialization_ext_std_optional.cpp b/tests/serialization_ext_std_optional.cpp index e87beb1..ef179ab 100644 --- a/tests/serialization_ext_std_optional.cpp +++ b/tests/serialization_ext_std_optional.cpp @@ -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); }); }); diff --git a/tests/serialization_ext_std_set.cpp b/tests/serialization_ext_std_set.cpp index 0d1db6e..92990b9 100644 --- a/tests/serialization_ext_std_set.cpp +++ b/tests/serialization_ext_std_set.cpp @@ -66,12 +66,12 @@ TEST(SerializeExtensionStdSet, FunctionSyntax) { SerializationContext ctx1; std::unordered_multiset t1{54,-484,841,79}; std::unordered_multiset 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)); diff --git a/tests/serialization_ext_std_smart_ptr.cpp b/tests/serialization_ext_std_smart_ptr.cpp index d445d0a..6e4b23e 100644 --- a/tests/serialization_ext_std_smart_ptr.cpp +++ b/tests/serialization_ext_std_smart_ptr.cpp @@ -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( @@ -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( @@ -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(bitsery::ext::PolymorphicClassesList{}); 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(bitsery::ext::PolymorphicClassesList{}); @@ -438,10 +436,10 @@ TEST_F(SerializeExtensionStdSmartSharedPtr, SameSharedObjectIsSerializedOnce) { std::shared_ptr baseData1{new Derived{3, 78}}; std::shared_ptr 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 baseData1{new Derived{3, 78}}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData1, StdSmartPtr{}); std::shared_ptr 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 baseData1{new Derived{3, 78}}; std::shared_ptr baseData2{new Derived{55, 11}}; std::shared_ptr 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 baseRes1{}; std::shared_ptr baseRes2{}; std::shared_ptr 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 baseData1{new Derived{3, 78}}; std::weak_ptr baseData11{baseData1}; std::weak_ptr 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 baseRes1{}; std::weak_ptr baseRes11{}; std::weak_ptr 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 baseData1{new MyStruct1{3, 78}}; std::weak_ptr baseData11{baseData1}; std::weak_ptr 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 baseRes1{}; std::weak_ptr baseRes11{}; std::weak_ptr 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 baseData1{}; std::weak_ptr baseData2{}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData2, StdSmartPtr{}); ser.ext(baseData1, StdSmartPtr{}); std::shared_ptr baseRes1{new Base{}}; std::weak_ptr 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 baseData1{}; std::weak_ptr baseData2{}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData2, StdSmartPtr{}); ser.ext(baseData1, StdSmartPtr{}); std::shared_ptr baseRes1{new MyStruct2{MyStruct2::MyEnum::V4, {1, 87}}}; std::weak_ptr 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 baseData2{}; std::weak_ptr baseData3{}; std::weak_ptr 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 baseRes2{new Derived{3, 78}}; std::weak_ptr baseRes3{baseRes2}; std::weak_ptr 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 baseData1{new Derived{3, 78}}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData1, StdSmartPtr{}); std::shared_ptr 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 baseData1{new Derived{3, 78}}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData1, StdSmartPtr{}); std::shared_ptr 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 tmp{new Derived{3, 78}}; std::weak_ptr 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 baseData1{new Derived{3, 78}}; - auto& ser = createSerializer(); + auto ser = createSerializer(); ser.ext(baseData1, StdSmartPtr{}); std::weak_ptr baseRes1{}; - auto& des = createDeserializer(); + auto des = createDeserializer(); des.ext(baseRes1, StdSmartPtr{}); EXPECT_FALSE(isPointerContextValid()); diff --git a/tests/serialization_ext_std_tuple.cpp b/tests/serialization_ext_std_tuple.cpp index 99db4fb..805d940 100644 --- a/tests/serialization_ext_std_tuple.cpp +++ b/tests/serialization_ext_std_tuple.cpp @@ -46,7 +46,6 @@ TEST(SerializeExtensionStdTuple, ValueTypesCanBeSerializedWithLambdaAndOrCallabl std::tuple 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{} }); }; - 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 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)); diff --git a/tests/serialization_ext_std_variant.cpp b/tests/serialization_ext_std_variant.cpp index 40a5e1f..2baafd2 100644 --- a/tests/serialization_ext_std_variant.cpp +++ b/tests/serialization_ext_std_variant.cpp @@ -77,10 +77,8 @@ TEST(SerializeExtensionStdVariant, ValueTypesCanBeSerializedWithLambdaAndOrCalla s.value4b(v); }; - auto& ser = ctx.createSerializer(); - ser.ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue{}}); - auto& des = ctx.createDeserializer(); - des.ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue{}}); + ctx.createSerializer().ext(t1, bitsery::ext::StdVariant{fncFloat, OverloadValue{}}); + ctx.createDeserializer().ext(r1, bitsery::ext::StdVariant{fncFloat, OverloadValue{}}); 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 t2{}; diff --git a/tests/serialization_ext_value_range.cpp b/tests/serialization_ext_value_range.cpp index e303ac2..82a6edd 100644 --- a/tests/serialization_ext_value_range.cpp +++ b/tests/serialization_ext_value_range.cpp @@ -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)); } diff --git a/tests/serialization_objects.cpp b/tests/serialization_objects.cpp index 2d5402e..e823ba6 100644 --- a/tests/serialization_objects.cpp +++ b/tests/serialization_objects.cpp @@ -69,12 +69,12 @@ void serialize(S& s, X& o) template void serialize(S& s, Y& o) { - auto writeInt = [&s]( int& v) { s.template value(v); }; + auto writeInt = [](S& s, int& v) { s.template value(v); }; s.template text<1>(o.s, 10000); s.template value(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); diff --git a/tests/serialization_size.cpp b/tests/serialization_size.cpp index 16f4139..df4205f 100644 --- a/tests/serialization_size.cpp +++ b/tests/serialization_size.cpp @@ -28,9 +28,11 @@ using testing::Eq; bool SerializeDeserializeContainerSize(SerializationContext& ctx, const size_t size) { std::vector 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; } diff --git a/tests/serialization_test_utils.h b/tests/serialization_test_utils.h index b4b008a..59d386a 100644 --- a/tests/serialization_test_utils.h +++ b/tests/serialization_test_utils.h @@ -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; using InputAdapter = bitsery::InputBufferAdapter; using OutputAdapter = bitsery::OutputBufferAdapter; @@ -100,34 +96,59 @@ using Reader = bitsery::AdapterReader; template class BasicSerializationContext { public: - using TWriter = bitsery::AdapterWriter; - using TReader = bitsery::AdapterReader; - using TSerializer = bitsery::BasicSerializer; - using TDeserializer = bitsery::BasicDeserializer; + using TWriter = bitsery::AdapterWriter; + using TReader = bitsery::AdapterReader; + using TSerializer = bitsery::BasicSerializer; + using TDeserializer = bitsery::BasicDeserializer; + using TSerializerBPEnabled = typename TSerializer::BPEnabledType; + using TDeserializerBPEnabled = typename TDeserializer::BPEnabledType; Buffer buf{}; - std::unique_ptr ser{}; - std::unique_ptr> des{}; - TWriter* bw{}; - TReader* br{}; + std::unique_ptr bw{}; + std::unique_ptr br{}; - TSerializer& createSerializer(Context* ctx = nullptr) { - if (!ser) { - ser = std::unique_ptr(new TSerializer(OutputAdapter{buf}, ctx)); - bw = &bitsery::AdapterAccess::getWriter(*ser); + template ::value>::type* = nullptr> + TSerializer createSerializer() { + if (!bw) { + bw = std::unique_ptr(new TWriter{OutputAdapter{buf}}); } - return *ser; - }; + return TSerializer{*bw}; + } - TDeserializer & createDeserializer(Context* ctx = nullptr) { - bw->flush(); - if (!des) { - des = std::unique_ptr( - new TDeserializer(InputAdapter{buf.begin(), bw->writtenBytesCount()}, ctx)); - br = &bitsery::AdapterAccess::getReader(*des); + template + TSerializer createSerializer(typename std::enable_if::value, T>::type& ctx) { + if (!bw) { + bw = std::unique_ptr(new TWriter{OutputAdapter{buf}, ctx}); } - return *des; - }; + return TSerializer{*bw}; + } + + + template ::value>::type* = nullptr> + TDeserializer createDeserializer() { + size_t writtenBytes = 0; + if (bw) { + bw->flush(); + writtenBytes = bw->writtenBytesCount(); + } + if (!br) { + br = std::unique_ptr(new TReader{InputAdapter{buf.begin(), writtenBytes}}); + } + return TDeserializer{*br}; + } + + template + TDeserializer createDeserializer(typename std::enable_if::value, T>::type& ctx) { + size_t writtenBytes = 0; + if (bw) { + bw->flush(); + writtenBytes = bw->writtenBytesCount(); + } + if (!br) { + br = std::unique_ptr(new TReader{InputAdapter{buf.begin(), writtenBytes}, ctx}); + } + return TDeserializer{*br}; + } size_t getBufferSize() const { return bw->writtenBytesCount(); diff --git a/tests/serialization_text.cpp b/tests/serialization_text.cpp index 9b9a514..ed23287 100644 --- a/tests/serialization_text.cpp +++ b/tests/serialization_text.cpp @@ -118,4 +118,12 @@ TEST(SerializeText, WhenCArrayNotNullterminatedThenAssert) { t1[CARR_LENGTH-1] = 'x'; EXPECT_DEATH(ctx.createSerializer().text<2>(t1), ""); } -#endif \ No newline at end of file +#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)); +} \ No newline at end of file