diff --git a/CHANGELOG.md b/CHANGELOG.md index 911a24d..435aed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,19 +2,38 @@ ### Features -* added **SERIALIZE_FRIEND** macro to be able to serialize private struct fields. +* new function **growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers. + * old consumer: correctly read old interfce and ignore new data. + * new consumer: get defaults (zero values) for new fields, when reading old data. +* new **SERIALIZE_FRIEND** macro that enables to serialize private object fields. * friendly static_assert message when serializing **object**, that doesn't have **serialize** function defined. -* added **custom** function to override default behaviour for **object** serialization. -* renamed function **ext** to **extension** and changed its interface, to make it more easy to extend. +* added **object** overload, that invokes user function/lambda with object. It is the same as calling user function directly, but makes more consistent API. +* Serializer/Deserializer now have optional *context* parameter, that might be required in some specific serialization cases. * improved serialization performance: added support for fixed size buffer for best performance. +* improved performance for reading/writing container sizes. +* added new method to BufferReader **getError** which returns on of three values: NO_ERROR, BUFFER_OVERFLOW, INVALID_BUFFER_DATA, also added setError method, that is only used by Deserializer. ### Breaking changes +* container and text sizes representation changed, to allow much faster size reads/writes for small values. +* renamed functions: + * **ext** to **extend** and changed its interface, to make it more easy to extend. + * alias functions that write bytes directly no has *b* (meaning bytes) at the end of the name eg. *value4* now is *value4b*. + * **substitution** changed to **entropy**, to sound more familiar to *entropy encoding* term. * now all serializer/deserializer functions return void, to avoid undefined behaviour for functions parameters evaluation when using method chaining. There was no benefits apart from *nicer* syntax, but could have undefined behaviour when building complex serialization flows. +* removed **array** and added fixed sizes overloads for **container**. * changed BufferWriter/Reader behaviour: * added *FixedBufferSize* config bool parameter for *BufferWriter* for better serializer performance (more than 50% improvement). Default config is resizable buffer (*std::vector*). * after serialization, call *getWrittenRange* to get valid range written to buffer, because BufferWritter for resizable buffer now always resize to *capacity* to avoid using *back_insert_iterator* for better performance. - * BufferReader only has constructors with iterators (range). + * BufferReader has constructor with iterators (range), and raw value type pointers (begin, end). +* removed **isValid** method from Deserializer, only BufferReader/Writer store states. +* renamed BufferReader **isCompleted** to **isCompletedSuccessfully**, that returns true only when there is no errors and buffer is fully read. + +### Bug fixes + +* **readBytes** was reading in aligned mode, when scratch value from previous bit operations was zero. + + # [2.0.1](https://github.com/fraillt/bitsery/compare/v2.0.0...v2.0.1) (2017-08-12) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ad670c6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing to Bitsery + +So you want to contribute something to Bitsery? That's great! Contributions are always very much appreciated, whether it's a bug fix, a new feature - or just a fix to the documentation. +However, to make sure the process of accepting patches goes smoothly, you should try to follow these few simple guidelines when +you contribute: + +1. Fork the repository. +2. Create new branch based on the *master* branch (`git checkout -b your_branch master`). If your contribution is a bug fix, you should name your branch `bugfix/xxx`; for a feature, it should be `feature/xxx`. Otherwise, just use your good judgment. Consistent naming of branches is appreciated since it makes the output of `git branch` easier to understand with a single glance. +3. Do your modifications on that branch. Except for special cases, your contribution should include proper unit tests and documentation. +4. Make sure your modifications did not break anything by building, running tests and checking code coverage (test coverage should not be less than 100%): + ```shell + mkdir build + cd build + cmake .. + make + ctest + make tests_coverage + x-www-browser ./coverage/index.html + ``` +5. Commit your changes, and push to your fork (`git push origin your_branch`). Commit message should be one line short description. When applicable, please squash adjacent *wip* commits into a single *logical* commit. +6. Open a pull request against Bitsery *master* branch. Currently ongoing development is on *master*. At some point an integration branch will be set-up, and pull-requests should target that, but for now its all against master. You may see feature branches come and go, too. + + +## Style guide + +Just use your own judgment and stick to the style of the surrounding code. \ No newline at end of file diff --git a/README.md b/README.md index e842360..152d6c7 100644 --- a/README.md +++ b/README.md @@ -4,84 +4,32 @@ [![Join the chat at https://gitter.im/bitsery/Lobby](https://badges.gitter.im/bitsery/Lobby.svg)](https://gitter.im/bitsery/Lobby) Header only C++ binary serialization library. -It is designed around the networking requirements for multiplayer real-time fast paced games as first person shooters. +It is designed around the networking requirements for real-time data delivery, especially for games. - -All cross-platform requirements are enforced at compile time, so serialized data do not store any run-time type information and is as small as possible. +All cross-platform requirements are enforced at compile time, so serialized data do not store any meta-data information and is as small as possible. > **bitsery** is looking for your feedback on [gitter](https://gitter.im/bitsery/Lobby) ## Features -* Cross-platform compatible +* Cross-platform compatible. +* Optimized for speed and space. +* No code generation required: no IDL or metadata, use your types directly. * 2-in-1 declarative control flow, same code for serialization and deserialization. +* Provides forward/backward compatibility for your types. +* Allows fine-grained serialization control using advanced serialization techniques like value ranges, entrophy encoding etc... +* Extendable for types that requires different serialization and deserialization logic (e.g. pointers, or geometry compression). * Configurable endianess support. -* Advanced serialization features like value ranges and entrophy encoding. -* Easy to extend for types that requires different serialization and deserialization logic (e.g. pointers, or geometry compression). -* No exceptions. Error checking at runtime for deserialization, and asserts on serialization. + +## Why to use bitsery +Read more about the "why" in library [motivation](doc/design/README.md) section. ## How to use it This documentation comprises these parts: * [Tutorial](doc/tutorial/README.md) - getting started. * [Reference section](doc/README.md) - all the details. - -## Example -```cpp -#include - -enum class MyEnum:uint16_t { V1,V2,V3 }; -struct MyStruct { - uint32_t i; - MyEnum e; - std::vector fs; -}; - -//define how object should be serialized/deserialized -SERIALIZE(MyStruct) { - s.value4(o.i); - s.value2(o.e); - s.container4(o.fs, 10); -}; - -using namespace bitsery; - -int main() { - //set some random data - MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}}; - MyStruct res{}; - - //create serializer - //1) create buffer to store data - std::vector buffer; - //2) create buffer writer that is able to write bytes or bits to buffer - BufferWriter bw{buffer}; - //3) create serializer - Serializer ser{bw}; - - //serialize object, can also be invoked like this: serialize(ser, data) - ser.object(data); - - //flush to buffer, before creating buffer reader - bw.flush(); - - //create deserializer - //1) create buffer reader - BufferReader br{bw.getWrittenRange()}; - //2) create deserializer - Deserializer des{br}; - - //deserialize same object, can also be invoked like this: serialize(des, data) - des.object(res); - assert(data.fs == res.fs && data.i == res.i && data.e == res.e); -} -``` - -## Todo list - -> Support wider range for underlying buffer value type for BufferWriter and BufferReader (currently must be unsigned 1byte, e.g. uint8_t). - -> Delta serialization and deserialization is in progress. +## Requirements ## Platforms diff --git a/doc/README.md b/doc/README.md index 97e40da..0df9a04 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,34 +2,50 @@ To get the most out of **Bitsery**, start with the [tutorial](tutorial/README.md Once you're familiar with the library consider the following reference material. Library design: -* [*valueN* instead of *value*](design/function_n.md) -* [fundamental types](design/fundamental_types.md) -* [serializer/deserializer functions overloads](design/function_overload.md) -* [extending library functionality](design/extensions.md) -* [errors handling](design/errors.md) +* `valueNb instead of value` +* `fundamental types` +* `serializer/deserializer functions overloads` +* `extending library functionality` +* `errors handling` +* `forward/backward compatibility via growable` -Serializer/Deserializer functions (alphabetical order): -* [align](fnc_array.md) -* [array](fnc_array.md) -* [boolBit/Byte](fnc_bool.md) -* [container](fnc_container.md) -* [custom](fnc_custom.md) -* [extension](fnc_extension.md) -* [isValid](reference/fnc_is_valid.md) -* [object](fnc_object.md) -* [range](fnc_range.md) -* [substitution](fnc_substitution.md) -* [text](fnc_text.md) -* [value](fnc_value.md) +Core Serializer/Deserializer functions (alphabetical order): +* `boolByte` +* `container` +* `object` +* `text` +* `value` + +Advanced Serializer/Deserializer functions (alphabetical order): +* `align` +* `boolBit` +* `entropy` +* `extend` +* `growable` +* `range` BasicBufferWriter/Reader functions: -* [writeBits](bb_write_bits.md) +* `writeBits/readBits` +* `writeBytes/readBytes` +* `writeBuffer/readBuffer` +* `align` +* `beginSession/endSession` +* `flush (writer only)` +* `setError (reader only)` +* `getError (reader only)` +* `isCompletedSuccessfully (reader only)` + + Tips and tricks: +* if you're getting static assert "please define 'serialize' function", most likely it is because your SERIALIZE function is not defined in same namespace as object. -Advanced topics: +Limitations: +* max **text** or **container** size can be 2^(n-2) (where n = sizeof(std::size_t) * 8) for 32-bit systems it is 1073741823 (0x3FFFFFF). +* when using **growable** serialized buffer cannot be greater than 2^(n-2) (where n = sizeof(std::size_t) * 8). Other: -* [Why Bitsery?](why-bitsery.md) * [Contributing](../CONTRIBUTING.md) * [Change log](../CHANGELOG.md) + + diff --git a/doc/design/README.md b/doc/design/README.md new file mode 100644 index 0000000..ed59b61 --- /dev/null +++ b/doc/design/README.md @@ -0,0 +1,27 @@ +## Motivation + +Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++. +Most well-known serialization libraries are *too fat* and tries to solve too many things by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..) while in the process becomes hard to use, are memory or/and speed inefficient. + +The best alternative that I was able to find is [flatbuffers](https://google.github.io/flatbuffers/). +It is fast, memory efficient, and [comparing with other alternatives](https://google.github.io/flatbuffers/flatbuffers_benchmarks.html) looks like *de facto* choice for games. +While Flatbuffers is designed with multiple programming languages support, bitsery is designed specifically for C++. + +## A word about JSON + +People use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format. +Although JSON is very readable and very convenient when used together with dynamically typed languages (such as JavaScript). +When serializing data from statically typed languages, however, JSON not only has the obvious drawback of runtime inefficiency, but also forces you to write more code to access data (counterintuitively) due to its dynamic-typing serialization system. +It's also a text format,- human readable, but space inefficient. + +Adding optional support for JSON doesn't come for free either. +When there is no multi-language support, we no longer require IDL(interface definition language) to define schemas so we could have consistent interface across multiple languages. +When we no longer have code generation, it becomes imposibble to support JSON *for free* without defining additional metadata, because C++ doesn't have a reflection system (although static reflection was proposed to standard recently). + +So, to avoid unnecessary library complexity it is best to forget about JSON, and stick with what machines and C++ is good at,- binary format. + +Bitsery is a result of what you can get, when you sacrifice multi-language support and JSON format, but take other *goodies*. + +# Bitsery + +*todo* \ No newline at end of file diff --git a/doc/reference/buf_get_error.md b/doc/reference/buf_get_error.md new file mode 100644 index 0000000..803f2c3 --- /dev/null +++ b/doc/reference/buf_get_error.md @@ -0,0 +1,6 @@ +*document in progress* +* NO_ERROR, +* BUFFER_OVERFLOW, +* INVALID_BUFFER_DATA +* write what happens when data is corrupted +* how growable effect error codes when deserializing old data format. diff --git a/doc/reference/buf_is_completed.md b/doc/reference/buf_is_completed.md deleted file mode 100644 index e69de29..0000000 diff --git a/doc/reference/buf_is_completed_successfully.md b/doc/reference/buf_is_completed_successfully.md new file mode 100644 index 0000000..d5976eb --- /dev/null +++ b/doc/reference/buf_is_completed_successfully.md @@ -0,0 +1,7 @@ +### BufferReader.isCompletedSuccessfully() + +Returns true when buffer was fully read, and there was no [errors](buf_get_error.md). + +If buffer contains multiple serialized objects and you want to know deserialization state for each object, then use [getError](buf_get_error.md) after reading each object. + +Use this function when buffer contains one object, or you know that you read all data. \ No newline at end of file diff --git a/doc/reference/fnc_is_valid.md b/doc/reference/fnc_is_valid.md deleted file mode 100644 index e69de29..0000000 diff --git a/doc/tutorial/composition.md b/doc/tutorial/composition.md index e69de29..848ee7f 100644 --- a/doc/tutorial/composition.md +++ b/doc/tutorial/composition.md @@ -0,0 +1,3 @@ +*document in progress* +* explain why *value* and *object* is fundamental functions. +* write about **growable** and **customize** \ No newline at end of file diff --git a/doc/tutorial/hello_world.md b/doc/tutorial/hello_world.md index 15eed88..d8d7229 100644 --- a/doc/tutorial/hello_world.md +++ b/doc/tutorial/hello_world.md @@ -88,10 +88,10 @@ struct Player { using namespace bitsery; void serialize(Serializer& s, const Player& data) { - s.value4(data.pos.x); - s.value4(data.pos.y); - s.value4(data.pos.z); - s.text1(data.name); + s.value4b(data.pos.x); + s.value4b(data.pos.y); + s.value4b(data.pos.z); + s.text1b(data.name); } int main() { @@ -127,7 +127,7 @@ Let's look at the code, how we did this. There are three distinct parts that participate in serialization process. * Buffer - container that we store our serialized data, in our case vector. * BufferWriter - resposible for writing bytes and bits to *Buffer*, it also makes sure that it is portable across Little and Big endian systems. -* Serializer - extendable interface that converts any type to bytes or bits, and use *BufferWriter* to write them. Serializer interface also ensures that code is portable at compile time. This means, that if your serialization code compiles on other platform, it will be 100% correct. +* Serializer - extendable interface that converts any type to bytes or bits, and use *BufferWriter* to write them. Serializer object does not store any state, it only forwards all calls to BufferWritter, further more it ensures that code is portable at compile time. This means, that if your serialization code compiles on other platform, it will be 100% correct. ```cpp std::vector buf; BufferWriter bw{buf}; @@ -135,20 +135,20 @@ There are three distinct parts that participate in serialization process. ``` Serialization function is very readable, and explicitly express intent what and how to serialize: -* *value4* serialize [fundamental type](../design/fundamental_types.md) (ints, floats, chars, enums) of 4 bytes. -* *text1* effectively serialize text, and underlying text type is 1byte per letter. +* *value4b* serialize [fundamental type](../design/fundamental_types.md) (ints, floats, chars, enums) of 4 bytes. +* *text1b* effectively serialize text, and underlying text type is 1byte per letter. ```cpp - s.value4(data.pos.x); - s.value4(data.pos.y); - s.value4(data.pos.z); - s.text1(data.name); + s.value4b(data.pos.x); + s.value4b(data.pos.y); + s.value4b(data.pos.z); + s.text1b(data.name); ``` -> learn more about why you need to write [value4 instead of value](../design/function_n.md). +> learn more about why you need to write [value4b instead of value](../design/function_n.md). -Finally, before getting serialized data you must *flush* BufferWriter, it writes any remaining bits to buffer. In our case it is not required, because we only worked with whole bytes, but it is good practice to always use it after finishing serialization. +Finally, before getting serialized data you must *flush* BufferWriter, it writes any remaining bits to buffer and additional data for types that require forward/backward compatibility. In our case it is not required, because we only worked with whole bytes, but it is good practice to always use it after finishing serialization. To actually get written data you must call *getWrittenRange*, it return begin/end iterators to our buffer (*std::vector buf*), for performance reasons BufferWritter always resizes underlying buffer to *capacity* so it could use containers iterator to update data, instead of back_insert_iterator to insert data. diff --git a/doc/tutorial/two_in_one.md b/doc/tutorial/two_in_one.md index 38cc33a..0faeb99 100644 --- a/doc/tutorial/two_in_one.md +++ b/doc/tutorial/two_in_one.md @@ -9,10 +9,10 @@ So one way to make this happen is to have *Serializer/Deserializer* as template ```cpp template void serialize(S& s, Player& o) { - s.value4(o.pos.x); - s.value4(o.pos.y); - s.value4(o.pos.z); - s.text1(o.name); + s.value4b(o.pos.x); + s.value4b(o.pos.y); + s.value4b(o.pos.z); + s.text1b(o.name); } ``` @@ -53,10 +53,10 @@ struct Player { using namespace bitsery; SERIALIZE(Player) { - s.value4(o.pos.x); - s.value4(o.pos.y); - s.value4(o.pos.z); - s.text1(o.name); + s.value4b(o.pos.x); + s.value4b(o.pos.y); + s.value4b(o.pos.z); + s.text1b(o.name); } Player createData() { @@ -86,8 +86,7 @@ int main() { serialize(des, res); - std::cout << "deserializer state: " << des.isValid() << std::endl - << "buffer completed: " << br.isCompleted() << std::endl + std::cout << "buffer completed successfully: " << br.isCompletedSuccessfully() << std::endl << "pos equals: " << (res.pos == data.pos) << std::endl << "name equals: " << (strcmp(res.name, data.name) == 0); return 0; @@ -95,8 +94,7 @@ int main() { ``` ```bash -deserializer state: 1 -buffer completed: 1 +buffer completed successfully: 1 pos equals: 1 name equals: 1 ``` @@ -105,14 +103,14 @@ We created *Deserializer* and modified *serialize* function to accept *Serialize Deserialization is very similar as serialization, it also consists of three separate components: * Buffer - container that we read data from, in our case *vector*. -* BufferReader - reads bytes and bits from *Buffer*, it also makes sure that it is portable across Little and Big endian systems. -* Deserializer - same interface as *Serializer* that use *BufferReader* to read bits and bytes, and convert to specific type. Deserializer also checks for errors at runtime, because data might come from untrusted source and can terminate program with buffer-overflow or segmentation fault if we are not careful. +* BufferReader - reads bytes and bits from *Buffer*, it makes sure that it is portable across Little and Big endian systems and also checks for errors at runtime, because data might come from untrusted source and can terminate program with buffer-overflow or segmentation fault if we are not careful. +* Deserializer - same interface as *Serializer* but forward all data to *BufferReader* to read bits and bytes. Since deserialization involves error checking there are two additional functions to check if everything is correct after deserialization. -* [BufferReader.isCompleted()](../reference/buf_is_completed.md) - returns true, if whole buffer was read during deserialization. -* [Deserializer.isValid()](../reference/fnc_is_valid.md) - returns true, if there was no errors during deserialization. +* [BufferReader.isCompletedSuccessfully()](../reference/buf_is_completed_successfully.md) - returns true, if whole buffer was read during deserialization and no errors was found. +* [BufferReader.getError()](../reference/buf_get_error.md) - returns current buffer reader state. Useful when buffer contains more than one object, and you want to check each objects deserialization state separately. -One thing to note about BufferReader is that it doesn't have constructor that accepts buffer directly. Instead it only accepts begin/end iterators, because it needs to know precise data buffer length, to correctly use *isComplete* function. +One thing to note about BufferReader is that it doesn't have constructor that accepts buffer directly. Instead it only accepts begin/end iterators, because it needs to know precise data buffer length, to correctly use *isCompleteSuccessfully* function. ```cpp BufferReader br{bw.getWrittenRange()}; diff --git a/examples/basic_usage.cpp b/examples/basic_usage.cpp index c1a4bd6..fc709e3 100644 --- a/examples/basic_usage.cpp +++ b/examples/basic_usage.cpp @@ -9,9 +9,9 @@ struct MyStruct { //define how object should be serialized/deserialized SERIALIZE(MyStruct) { - s.value4(o.i); - s.value2(o.e); - s.container4(o.fs, 10); + s.value4b(o.i); + s.value2b(o.e); + s.container4b(o.fs, 10); }; using namespace bitsery; diff --git a/include/bitsery/bitsery.h b/include/bitsery/bitsery.h index 0335e6b..dd61f77 100644 --- a/include/bitsery/bitsery.h +++ b/include/bitsery/bitsery.h @@ -24,8 +24,8 @@ #ifndef BITSERY_BITSERY_H #define BITSERY_BITSERY_H -#define BITSERY_MAJOR_VERSION 2 -#define BITSERY_MINOR_VERSION 1 +#define BITSERY_MAJOR_VERSION 3 +#define BITSERY_MINOR_VERSION 0 #define BITSERY_PATCH_VERSION 0 #define BITSERY_QUOTE_MACRO(name) #name diff --git a/include/bitsery/buffer_reader.h b/include/bitsery/buffer_reader.h index 7488491..04d2c1c 100644 --- a/include/bitsery/buffer_reader.h +++ b/include/bitsery/buffer_reader.h @@ -27,18 +27,23 @@ #include "common.h" #include +#include namespace bitsery { template struct BasicBufferReader { + using BufferType = typename Config::BufferType; using ValueType = typename BufferType::value_type; - using IteratorType = typename BufferType::iterator; + using BufferIteratorType = typename BufferType::iterator; using ScratchType = typename details::SCRATCH_TYPE::type; - BasicBufferReader(IteratorType begin, IteratorType end) - :_pos{begin}, _end{end} { + BasicBufferReader(ValueType* begin, ValueType* end) + :_pos{begin}, + _end{end}, + _session{*this, _pos, _end} + { static_assert(std::is_unsigned(), "Config::BufferValueType must be unsigned"); static_assert(std::is_unsigned(), "Config::BufferScrathType must be unsigned"); static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType), @@ -46,8 +51,13 @@ namespace bitsery { static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte"); } - BasicBufferReader(BufferRange range) - :BasicBufferReader(range.begin(), range.end()) {} + BasicBufferReader(BufferRange range) + :BasicBufferReader(std::addressof(*range.begin()), std::addressof(*range.end())) { + static_assert(std::is_same< + typename std::iterator_traits::iterator_category, + std::random_access_iterator_tag>::value, + "BufferReader only accepts random access iterators"); + } BasicBufferReader(const BasicBufferReader &) = delete; @@ -61,82 +71,115 @@ namespace bitsery { template - bool readBytes(T &v) { + void readBytes(T &v) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); using UT = typename std::make_unsigned::type; - return !m_scratch - ? directRead(&v, 1) - : readBits(reinterpret_cast(v), details::BITS_SIZE); + if (!m_scratchBits) + directRead(&v, 1); + else + readBits(reinterpret_cast(v), details::BITS_SIZE); } template - bool readBuffer(T *buf, size_t count) { + void readBuffer(T *buf, size_t count) { static_assert(std::is_integral(), ""); static_assert(sizeof(T) == SIZE, ""); - if (!m_scratchBits) - return directRead(buf, count); - - using UT = typename std::make_unsigned::type; - //todo improve implementation - const auto end = buf + count; - for (auto it = buf; it != end; ++it) { - if (!readBits(reinterpret_cast(*it), details::BITS_SIZE)) - return false; + if (!m_scratchBits) { + directRead(buf, count); + } else { + using UT = typename std::make_unsigned::type; + //todo improve implementation + const auto end = buf + count; + for (auto it = buf; it != end; ++it) + readBits(reinterpret_cast(*it), details::BITS_SIZE); } - return true; } template - bool readBits(T &v, size_t bitsCount) { + void readBits(T &v, size_t bitsCount) { static_assert(std::is_integral() && std::is_unsigned(), ""); - - const auto bytesRequired = bitsCount > m_scratchBits - ? ((bitsCount - 1 - m_scratchBits) >> 3) + 1u - : 0u; - if (static_cast(std::distance(_pos, _end)) < bytesRequired) - return false; readBitsInternal(v, bitsCount); - return true; } - bool align() { + void align() { if (m_scratchBits) { ScratchType tmp{}; readBitsInternal(tmp, m_scratchBits); - return tmp == 0; + if (tmp) + setError(BufferReaderError::INVALID_BUFFER_DATA); } - return true; } - bool isCompleted() const { - return _pos == _end; + bool isCompletedSuccessfully() const { + return _pos == _end && !_session.hasActiveSessions(); + } + + BufferReaderError getError() const { + auto res = std::distance(_end, _pos); + if (res > 0) { + auto err = static_cast(res); + if (_session.hasActiveSessions() && err == BufferReaderError::BUFFER_OVERFLOW) + return BufferReaderError::NO_ERROR; + return err; + } + return BufferReaderError::NO_ERROR; + } + + void setError(BufferReaderError error) { + _end = _pos; + //to avoid creating temporary for error state, mark an error by passing _pos after the _end + std::advance(_pos, static_cast(error)); + } + + void beginSession() { + align(); + if (getError() != BufferReaderError::INVALID_BUFFER_DATA) { + _session.begin(); + } + } + + void endSession() { + align(); + if (getError() != BufferReaderError::INVALID_BUFFER_DATA) { + _session.end(); + } } private: - IteratorType _pos; - IteratorType _end; + ValueType* _pos; + ValueType* _end; + details::BufferSessionsReader, ValueType*> _session; + ScratchType m_scratch{}; + size_t m_scratchBits{}; ///< Number of bits currently in the scratch buffer. If the user wants to read more bits than this, we have to go fetch another dword from memory. template - bool directRead(T *v, size_t count) { + void directRead(T *v, size_t count) { static_assert(!std::is_const::value, ""); const auto bytesCount = sizeof(T) * count; - if (static_cast(std::distance(_pos, _end)) < bytesCount) - return false; - //read from buffer, to data ptr, - std::copy_n(_pos, bytesCount, reinterpret_cast(v)); - std::advance(_pos, bytesCount); - //swap each byte if nessesarry - _swapDataBits(v, count, std::integral_constant{}); - return true; + + if (std::distance(_pos, _end) >= static_cast(bytesCount)) { + + std::memcpy(reinterpret_cast(v), _pos, bytesCount); + _pos += bytesCount; + + //swap each byte if nessesarry + _swapDataBits(v, count, std::integral_constant{}); + } else { + //set everything to zeros + std::memset(v, 0, bytesCount); + + if (getError() == BufferReaderError::NO_ERROR) + setError(BufferReaderError::BUFFER_OVERFLOW); + } } template void _swapDataBits(T *v, size_t count, std::true_type) { - std::for_each(v, std::next(v, count), [this](T &v) { v = details::swap(v); }); + std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); }); } template @@ -166,9 +209,6 @@ namespace bitsery { v = res; } - ScratchType m_scratch{}; - size_t m_scratchBits{}; ///< Number of bits currently in the scratch buffer. If the user wants to read more bits than this, we have to go fetch another dword from memory. - }; //helper type using BufferReader = BasicBufferReader; diff --git a/include/bitsery/buffer_writer.h b/include/bitsery/buffer_writer.h index 1cdf494..8d60d98 100644 --- a/include/bitsery/buffer_writer.h +++ b/include/bitsery/buffer_writer.h @@ -55,11 +55,31 @@ namespace bitsery { } void align() { - _bitsCount += 8 - (_bitsCount % 8); + auto _scratch = (_bitsCount % 8); + _bitsCount += (8 - _scratch) % 8; } void flush() { - _bitsCount += (8 - (_bitsCount % 8)) % 8; + align(); + //flush sessions count + if (_sessionsBytesCount > 0) { + auto sessionsDataSizeBytesCount = (_sessionsBytesCount < 0x8000u ? 2 : 4); + _bitsCount += (_sessionsBytesCount + sessionsDataSizeBytesCount) * 8; + _sessionsBytesCount = 0; + } + } + + void beginSession() { + + } + + void endSession() { + auto endPos = getWrittenBytesCount(); + details::writeSize(*this, endPos); + auto sessionEndBytesCount = getWrittenBytesCount() - endPos; + //remove written bytes, because we'll write them at the end + _bitsCount -= sessionEndBytesCount * 8; + _sessionsBytesCount += sessionEndBytesCount; } //get size in bytes @@ -69,7 +89,7 @@ namespace bitsery { private: size_t _bitsCount{}; - + size_t _sessionsBytesCount{}; }; template @@ -79,7 +99,9 @@ namespace bitsery { using ScratchType = typename details::SCRATCH_TYPE::type; using BufferContext = details::WriteBufferContext; - explicit BasicBufferWriter(BufferType &buffer) : _bufferContext{buffer} { + explicit BasicBufferWriter(BufferType &buffer) + : _bufferContext{buffer} + { static_assert(std::is_unsigned(), "Config::BufferType::value_type must be unsigned"); static_assert(std::is_unsigned(), "Config::BufferScrathType must be unsigned"); static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType), @@ -134,23 +156,29 @@ namespace bitsery { } void align() { - if (_scratchBits) - writeBitsInternal(ValueType{}, details::BITS_SIZE - _scratchBits); + writeBitsInternal(ValueType{}, (details::BITS_SIZE - _scratchBits) % 8); } void flush() { - if (_scratchBits) { - auto tmp = static_cast( _scratch & _MASK ); - directWrite(&tmp, 1); - _scratch >>= _scratchBits; - _scratchBits -= _scratchBits; - } + align(); + _session.flushSessions(*this); } BufferRange getWrittenRange() const { return _bufferContext.getWrittenRange(); } + void beginSession() { + align(); + _session.begin(); + } + + void endSession() { + align(); + auto range = _bufferContext.getWrittenRange(); + _session.end(static_cast(std::distance(range.begin(), range.end()))); + } + private: template @@ -212,6 +240,7 @@ namespace bitsery { BufferContext _bufferContext; ScratchType _scratch{}; size_t _scratchBits{}; + details::BufferSessionsWriter _session{}; }; //helper type diff --git a/include/bitsery/common.h b/include/bitsery/common.h index f969b12..aa0f8c0 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -27,8 +27,6 @@ #include "details/buffer_common.h" #include -#include - namespace bitsery { struct DefaultConfig { diff --git a/include/bitsery/delta_deserializer.h b/include/bitsery/delta_deserializer.h deleted file mode 100644 index 7df6d7f..0000000 --- a/include/bitsery/delta_deserializer.h +++ /dev/null @@ -1,220 +0,0 @@ -//MIT License -// -//Copyright (c) 2017 Mindaugas Vinkelis -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. - - - -#ifndef BITSERY_DELTADESERIALIZER_H -#define BITSERY_DELTADESERIALIZER_H - -#include -#include -#include -#include "deserializer.h" - -namespace bitsery { - - template - class DeltaDeserializer { - public: - DeltaDeserializer(Reader &r, const TObj &oldObj, const TObj &newObj) - : _deserializer{r}, - _reader{r}, - _oldObj{oldObj}, - _newObj{newObj}, - _objMemPos(std::deque(1, details::ObjectMemoryPosition{oldObj, newObj})), - _isNewElement{false} { - }; - - template::value>::type * = nullptr> - DeltaDeserializer &value(T &v) { - if (getChangedState(v)) { - constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE; - _reader.template readBytes(v); - } - return *this; - } - - template - DeltaDeserializer &object(T &&obj) { - if (getChangedState(obj)) - serialize(*this, std::forward(obj)); - return *this; - } - - template - DeltaDeserializer &text(std::basic_string &str, size_t maxSize) { - if (getChangedState(str)) { - _deserializer.template text(str, maxSize); - } - return *this; - } - - template - DeltaDeserializer &text(T (&str)[N]) { - if (getChangedState(str)) { - _deserializer.template text(str); - } - return *this; - } - - - template - DeltaDeserializer &array(std::array &arr, Fnc &&fnc) { - if (getChangedState(arr)) { - if (!_isNewElement) { - const auto old = *_objMemPos.top().getOldObjectField(arr); - processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc); - } else { - for (auto &v:arr) - fnc(*this, v); - } - } - return *this; - } - - - template - DeltaDeserializer &array(T (&arr)[N], Fnc &&fnc) { - if (getChangedState(arr)) { - if (!_isNewElement) { - const auto old = *_objMemPos.top().getOldObjectField(arr); - T *tmp = arr; - processContainer(old, old + N, tmp, tmp + N, fnc); - } else { - T *tmp = arr; - for (auto i = 0u; i < N; ++i, ++tmp) - fnc(*this, *tmp); - } - } - return *this; - } - - template - DeltaDeserializer &container(T &obj, size_t maxSize, Fnc &&fnc) { - if (getChangedState(obj)) { - size_t newSize{}; - _reader.readBits(newSize, 32); - if (!_isNewElement) { - auto old = *_objMemPos.top().getOldObjectField(obj); - if (old.size() != newSize) - obj.resize(newSize); - processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj), - std::forward(fnc)); - } else { - obj.resize(newSize); - for (auto &v:obj) - fnc(*this, v); - } - } - return *this; - } - - private: - Deserializer _deserializer; - Reader &_reader; - - const TObj &_oldObj; - const TObj &_newObj; - std::stack _objMemPos; - bool _isNewElement; - - template - bool getChangedState(T &obj) { - if (!_isNewElement) { - if (!readChangedState()) { - obj = *_objMemPos.top().getOldObjectField(obj); - return false; - } - } - return true; - } - - template - bool getChangedState(T (&arr)[N]) { - if (!_isNewElement) { - if (!readChangedState()) { - auto old = *_objMemPos.top().getOldObjectField(arr); - auto end = arr + N; - auto pOld = old; - for (auto p = arr; p != end; ++p, ++pOld) - *p = *pOld; - return false; - } - } - return true; - } - - template - bool processContainer(TConstIt oldBegin, TConstIt oldEnd, TIt begin, TIt end, Fnc &&fnc) { - auto offset = readIndexOffset(); - auto p = begin; - auto pOld = oldBegin; - for (; p != end && pOld != oldEnd; ++p, ++pOld) { - if (offset) { - *p = *pOld; - --offset; - } else { - _objMemPos.emplace(details::ObjectMemoryPosition{*pOld, *p}); - fnc(*this, *p); - _objMemPos.pop(); - offset = readIndexOffset(); - } - } - if (offset != 0 && pOld != oldEnd) - return false; - _isNewElement = true; - for (; p != end; ++p, --offset) - fnc(*this, *p); - _isNewElement = false; - return offset == 0; - - } - - bool readChangedState() { - unsigned char res{}; - _reader.readBits(res, 1); - return res; - } - - - size_t readIndexOffset() { - //special case, if items are updated sequentialy - unsigned char tmp{}; - _reader.readBits(tmp, 1); - if (tmp) { - return 0u; - } else { - size_t res{}; - _reader.readBits(tmp, 1); - if (tmp > 0) - _reader.readBits(res, 4); - else - _reader.readBits(res, 32); - return res; - } - - } - - }; - -} -#endif //BITSERY_DELTADESERIALIZER_H diff --git a/include/bitsery/delta_serializer.h b/include/bitsery/delta_serializer.h deleted file mode 100644 index 2ad1360..0000000 --- a/include/bitsery/delta_serializer.h +++ /dev/null @@ -1,220 +0,0 @@ -//MIT License -// -//Copyright (c) 2017 Mindaugas Vinkelis -// -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files (the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions: -// -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. -// -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. - - - -#ifndef BITSERY_DELTASERIALIZER_H -#define BITSERY_DELTASERIALIZER_H - -#include -#include -#include -#include "serializer.h" - -namespace bitsery { - - template - class DeltaSerializer { - public: - DeltaSerializer(Writter &w, const TObj &oldObj, const TObj &newObj) - : _serializer{w}, - _writter{w}, - _oldObj{oldObj}, - _newObj{newObj}, - _objMemPos(std::deque(1, details::ObjectMemoryPosition{oldObj, newObj})), - _isNewElement{false} { - - }; - - template::value>::type * = nullptr> - DeltaSerializer &value(const T &v) { - if (setChangedState(v)) { - constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE; - _writter.template writeBytes(v); - } - return *this; - } - - template - DeltaSerializer &object(T &&obj) { - if (setChangedState(obj)) { - serialize(*this, std::forward(obj)); - } - return *this; - } - - template - DeltaSerializer &text(const std::basic_string &str, size_t maxSize) { - if (setChangedState(str)) { - _serializer.template text(str, maxSize); - } - return *this; - - } - - template - DeltaSerializer &text(const T (&str)[N]) { - if (setChangedState(str)) { - _serializer.template text(str); - } - return *this; - - } - - template - DeltaSerializer &array(const std::array &arr, Fnc &&fnc) { - if (setChangedState(arr)) { - if (!_isNewElement) { - const auto &old = *_objMemPos.top().getOldObjectField(arr); - processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc); - } else { - for (auto &v:arr) - fnc(*this, v); - } - } - return *this; - } - - - template - DeltaSerializer &array(const T (&arr)[N], Fnc &&fnc) { - if (setChangedState(arr)) { - if (!_isNewElement) { - auto old = *_objMemPos.top().getOldObjectField(arr); - const T *tmp = arr; - processContainer(old, old + N, tmp, tmp + N, fnc); - } else { - const T *tmp = arr; - for (auto i = 0u; i < N; ++i, ++tmp) - fnc(*this, *tmp); - } - } - return *this; - } - - template - DeltaSerializer &container(T &&obj, size_t maxSize, Fnc &&fnc) { - if (setChangedState(obj)) { - _writter.writeBits(obj.size(), 32); - if (!_isNewElement) { - auto old = *_objMemPos.top().getOldObjectField(obj); - processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj), - std::forward(fnc)); - } else { - for (auto &v:obj) - fnc(*this, v); - } - } - return *this; - } - - private: - Serializer _serializer; - Writter &_writter; - const TObj &_oldObj; - const TObj &_newObj; - std::stack _objMemPos; - bool _isNewElement; - - template - bool setChangedState(const T &obj) { - if (!_isNewElement) { - auto res = !_objMemPos.top().isFieldsEquals(obj); - writeChangedState(res); - return res; - } - return true; - } - - template - bool setChangedState(const T (&arr)[N]) { - if (!_isNewElement) { - auto old = *_objMemPos.top().getOldObjectField(arr); - auto end = arr + N; - bool changed{}; - for (auto p = arr, pOld = old; p != end; ++p, ++pOld) { - if (!(*p == *pOld)) { - changed = true; - break; - } - } - writeChangedState(changed); - return changed; - } - return true; - } - - - template - void processContainer(const T oldBegin, const T oldEnd, const T begin, const T end, Fnc &&fnc) { - auto misMatch = std::mismatch(oldBegin, oldEnd, begin, end); - auto lastChanged = begin; - while (misMatch.first != oldEnd && misMatch.second != end) { - writeIndexOffset(std::distance(lastChanged, misMatch.second)); - _objMemPos.emplace(details::ObjectMemoryPosition{*misMatch.first, *misMatch.second}); - fnc(*this, *misMatch.second); - _objMemPos.pop(); - ++misMatch.first; - ++misMatch.second; - lastChanged = misMatch.second; - misMatch = std::mismatch(misMatch.first, oldEnd, misMatch.second, end); - } - auto p = misMatch.second; - //write items left - writeIndexOffset(std::distance(lastChanged, end)); - //write old elements - for (auto pOld = misMatch.first; p != end && pOld != oldEnd; ++p, ++pOld) { - _objMemPos.emplace(details::ObjectMemoryPosition{*pOld, *p}); - fnc(*this, *p); - _objMemPos.pop(); - } - - //write new elements - _isNewElement = true; - for (; p != end; ++p) - fnc(*this, *p); - _isNewElement = false; - } - - void writeChangedState(bool state) { - _writter.writeBits(state ? 1u : 0u, 1); - } - - void writeIndexOffset(const size_t offset) { - //special case, if items are updated sequentialy - if (offset == 0) { - _writter.writeBits(1u, 1); - } else { - _writter.writeBits(0u, 1); - auto smallOffset = offset < 16; - _writter.writeBits(smallOffset ? 1u : 0u, 1); - if (smallOffset) - _writter.writeBits(offset, 4); - else - _writter.writeBits(offset, 32); - } - } - - }; - -} -#endif //BITSERY_DELTASERIALIZER_H diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index 4b1913c..12a00c3 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -35,7 +35,15 @@ namespace bitsery { template class Deserializer { public: - Deserializer(Reader &r) : _reader{r}, _isValid{true} {}; + Deserializer(Reader &r, void* context = nullptr) : _reader{r}, _context{context} {}; + + /* + * get serialization context. + * this is optional, but might be required for some specific deserialization flows. + */ + void* getContext() { + return _context; + } /* * object function @@ -43,10 +51,14 @@ namespace bitsery { template void object(T &&obj) { - if (_isValid) - details::SerializeFunction::invoke(*this, std::forward(obj)); + details::SerializeFunction::invoke(*this, std::forward(obj)); } + template + void object(T &&obj, Fnc &&fnc) { + fnc(*this, std::forward(obj)); + }; + /* * value overloads */ @@ -54,53 +66,48 @@ namespace bitsery { template::value>::type * = nullptr> void value(T &v) { static_assert(std::numeric_limits::is_iec559, ""); - if (_isValid) - _isValid = _reader.template readBytes(reinterpret_cast &>(v)); + _reader.template readBytes(reinterpret_cast &>(v)); } template::value>::type * = nullptr> void value(T &v) { using UT = std::underlying_type_t; - if (_isValid) - _isValid = _reader.template readBytes(reinterpret_cast(v)); + _reader.template readBytes(reinterpret_cast(v)); } template::value>::type * = nullptr> void value(T &v) { - if (_isValid) - _isValid = _reader.template readBytes(v); + _reader.template readBytes(v); } /* - * custom function + * growable function */ - template - void custom(T &&obj, Fnc &&fnc) { - if (_isValid) - fnc(*this, std::forward(obj)); + template + void growable(T&& obj, Fnc&& fnc) { + _reader.beginSession(); + fnc(*this, std::forward(obj)); + _reader.endSession(); }; /* - * extension functions + * extend functions */ template - void extension(T &obj, Ext &&ext, Fnc &&fnc) { - if (_isValid) - ext.deserialize(obj, *this, std::forward(fnc)); + void extend(T &obj, Ext &&ext, Fnc &&fnc) { + ext.deserialize(*this, _reader, obj, std::forward(fnc)); }; template - void extension(T &obj, Ext &&ext) { - if (_isValid) - ext.deserialize(obj, *this, [](auto &s, auto &v) { s.template value(v); }); + void extend(T &obj, Ext &&ext) { + ext.deserialize(*this, _reader, obj, [](auto &s, auto &v) { s.template value(v); }); }; template - void extension(T &obj, Ext &&ext) { - if (_isValid) - ext.deserialize(obj, *this, [](auto &s, auto &v) { s.object(v); }); + void extend(T &obj, Ext &&ext) { + ext.deserialize(*this, _reader, obj, [](auto &s, auto &v) { s.object(v); }); }; /* @@ -108,21 +115,17 @@ namespace bitsery { */ void boolBit(bool &v) { - if (_isValid) { - unsigned char tmp; - _isValid = _reader.readBits(tmp, 1); - v = tmp == 1; - } + uint8_t tmp{}; + _reader.readBits(tmp, 1); + v = tmp == 1; } void boolByte(bool &v) { - if (_isValid) { - unsigned char tmp; - _isValid = _reader.template readBytes<1>(tmp); - if (_isValid) - _isValid = tmp < 2; - v = tmp == 1; - } + unsigned char tmp; + _reader.template readBytes<1>(tmp); + if (tmp > 1) + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + v = tmp == 1; } /* @@ -131,51 +134,45 @@ namespace bitsery { template void range(T &v, const RangeSpec &range) { - if (_isValid) { - _isValid = _reader.readBits(reinterpret_cast &>(v), range.bitsRequired); - details::setRangeValue(v, range); - if (_isValid) - _isValid = details::isRangeValid(v, range); + _reader.readBits(reinterpret_cast &>(v), range.bitsRequired); + details::setRangeValue(v, range); + if (!details::isRangeValid(v, range)) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + v = range.min; } } /* - * substitution overloads + * entropy overloads */ template - void substitution(T &v, const std::array &expectedValues, Fnc &&fnc) { + void entropy(T &v, const T (&expectedValues)[N], Fnc &&fnc) { size_t index; range(index, {{}, N + 1}); - if (_isValid) { - if (index) - v = expectedValues[index - 1]; - else - fnc(*this, v); - } + if (index) + v = expectedValues[index - 1]; + else + fnc(*this, v); }; template - void substitution(T &v, const std::array &expectedValues) { + void entropy(T &v, const T (&expectedValues)[N]) { size_t index; range(index, {{}, N + 1}); - if (_isValid) { - if (index) - v = expectedValues[index - 1]; - else - value(v); - } + if (index) + v = expectedValues[index - 1]; + else + value(v); }; template - void substitution(T &v, const std::array &expectedValues) { + void entropy(T &v, const T (&expectedValues)[N]) { size_t index; range(index, {{}, N + 1}); - if (_isValid) { - if (index) - v = expectedValues[index - 1]; - else - object(v); - } + if (index) + v = expectedValues[index - 1]; + else + object(v); }; /* @@ -183,96 +180,96 @@ namespace bitsery { */ template - void text(std::basic_string &str, size_t maxSize) { + void text(T &str, size_t maxSize) { size_t size; readSize(size, maxSize); - if (_isValid) { - str.resize(size); - procContainer(std::begin(str), std::end(str), std::true_type{}); - } + str.resize(size); + procContainer(std::begin(str), std::end(str), std::true_type{}); } template void text(T (&str)[N]) { size_t size; readSize(size, N - 1); - if (_isValid) { - auto first = std::begin(str); - procContainer(first, std::next(first, size), std::true_type{}); - //null-terminated string - str[size] = {}; - } + auto first = std::begin(str); + procContainer(first, std::next(first, size), std::true_type{}); + //null-terminated string + str[size] = {}; } /* * container overloads */ + //dynamic size containers + template void container(T &&obj, size_t maxSize, Fnc &&fnc) { + static_assert(details::IsResizable::value, + "use container(const T&) overload without `maxSize` for static containers"); decltype(obj.size()) size{}; readSize(size, maxSize); - if (_isValid) { - obj.resize(size); - procContainer(std::begin(obj), std::end(obj), std::forward(fnc)); - } + obj.resize(size); + procContainer(std::begin(obj), std::end(obj), std::forward(fnc)); } template void container(T &obj, size_t maxSize) { + static_assert(details::IsResizable::value, + "use container(const T&) overload without `maxSize` for static containers"); decltype(obj.size()) size{}; readSize(size, maxSize); - if (_isValid) { - obj.resize(size); - procContainer(std::begin(obj), std::end(obj), std::false_type{}); - } + obj.resize(size); + procContainer(std::begin(obj), std::end(obj), std::false_type{}); } template void container(T &obj, size_t maxSize) { + static_assert(details::IsResizable::value, + "use container(const T&) overload without `maxSize` for static containers"); decltype(obj.size()) size{}; readSize(size, maxSize); - if (_isValid) { - obj.resize(size); - procContainer(std::begin(obj), std::end(obj)); - } + obj.resize(size); + procContainer(std::begin(obj), std::end(obj)); + } + //fixed size containers + + template::value>::type * = nullptr> + void container(T &&obj, Fnc &&fnc) { + static_assert(!details::IsResizable::value, + "use container(T&, size_t, Fnc) overload with `maxSize` for dynamic containers"); + procContainer(std::begin(obj), std::end(obj), std::forward(fnc)); } - /* - * array overloads (fixed size array (std::array, and c-style array)) - */ - - //std::array overloads - - template - void array(std::array &arr, Fnc &&fnc) { - procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); + template + void container(T &obj) { + static_assert(!details::IsResizable::value, + "use container(T&, size_t) overload with `maxSize` for dynamic containers"); + static_assert(VSIZE > 0); + procContainer(std::begin(obj), std::end(obj), std::false_type{}); } - template - void array(std::array &arr) { - procContainer(std::begin(arr), std::end(arr), std::true_type{}); - } - - template - void array(std::array &arr) { - procContainer(std::begin(arr), std::end(arr)); + template + void container(T &obj) { + static_assert(!details::IsResizable::value, + "use container(T&, size_t) overload with `maxSize` for dynamic containers"); + procContainer(std::begin(obj), std::end(obj)); } //c-style array overloads template - void array(T (&arr)[N], Fnc &&fnc) { + void container(T (&arr)[N], Fnc &&fnc) { procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); } template - void array(T (&arr)[N]) { + void container(T (&arr)[N]) { procContainer(std::begin(arr), std::end(arr), std::true_type{}); } template - void array(T (&arr)[N]) { + void container(T (&arr)[N]) { procContainer(std::begin(arr), std::end(arr)); } @@ -280,128 +277,108 @@ namespace bitsery { _reader.align(); } - bool isValid() const { - return _isValid; - } - //overloads for functions with explicit type size template - void value1(T &&v) { value<1>(std::forward(v)); } + void value1b(T &&v) { value<1>(std::forward(v)); } template - void value2(T &&v) { value<2>(std::forward(v)); } + void value2b(T &&v) { value<2>(std::forward(v)); } template - void value4(T &&v) { value<4>(std::forward(v)); } + void value4b(T &&v) { value<4>(std::forward(v)); } template - void value8(T &&v) { value<8>(std::forward(v)); } + void value8b(T &&v) { value<8>(std::forward(v)); } template - void extension1(T &v, Ext &&ext) { extension<1>(v, std::forward(ext)); }; + void extend1b(T &v, Ext &&ext) { extend<1>(v, std::forward(ext)); }; template - void extension2(T &v, Ext &&ext) { extension<2>(v, std::forward(ext)); }; + void extend2b(T &v, Ext &&ext) { extend<2>(v, std::forward(ext)); }; template - void extension4(T &v, Ext &&ext) { extension<4>(v, std::forward(ext)); }; + void extend4b(T &v, Ext &&ext) { extend<4>(v, std::forward(ext)); }; template - void extension8(T &v, Ext &&ext) { extension<8>(v, std::forward(ext)); }; + void extend8b(T &v, Ext &&ext) { extend<8>(v, std::forward(ext)); }; template - void substitution1(T &v, const std::array &expectedValues) { substitution<1>(v, expectedValues); }; + void entropy1b(T &v, const T (&expectedValues)[N]) { entropy<1>(v, expectedValues); }; template - void substitution2(T &v, const std::array &expectedValues) { substitution<2>(v, expectedValues); }; + void entropy2b(T &v, const T (&expectedValues)[N]) { entropy<2>(v, expectedValues); }; template - void substitution4(T &v, const std::array &expectedValues) { substitution<4>(v, expectedValues); }; + void entropy4b(T &v, const T (&expectedValues)[N]) { entropy<4>(v, expectedValues); }; template - void substitution8(T &v, const std::array &expectedValues) { substitution<8>(v, expectedValues); }; + void entropy8b(T &v, const T (&expectedValues)[N]) { entropy<8>(v, expectedValues); }; template - void text1(std::basic_string &str, size_t maxSize) { text<1>(str, maxSize); } + void text1b(T &str, size_t maxSize) { text<1>(str, maxSize); } template - void text2(std::basic_string &str, size_t maxSize) { text<2>(str, maxSize); } + void text2b(T &str, size_t maxSize) { text<2>(str, maxSize); } template - void text4(std::basic_string &str, size_t maxSize) { text<4>(str, maxSize); } + void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); } template - void text1(T (&str)[N]) { text<1>(str); } + void text1b(T (&str)[N]) { text<1>(str); } template - void text2(T (&str)[N]) { text<2>(str); } + void text2b(T (&str)[N]) { text<2>(str); } template - void text4(T (&str)[N]) { text<4>(str); } + void text4b(T (&str)[N]) { text<4>(str); } template - void container1(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } + void container1b(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } template - void container2(T &&obj, size_t maxSize) { container<2>(std::forward(obj), maxSize); } + void container2b(T &&obj, size_t maxSize) { container<2>(std::forward(obj), maxSize); } template - void container4(T &&obj, size_t maxSize) { container<4>(std::forward(obj), maxSize); } + void container4b(T &&obj, size_t maxSize) { container<4>(std::forward(obj), maxSize); } template - void container8(T &&obj, size_t maxSize) { container<8>(std::forward(obj), maxSize); } + void container8b(T &&obj, size_t maxSize) { container<8>(std::forward(obj), maxSize); } + + template + void container1b(T &&obj) { container<1>(std::forward(obj)); } + + template + void container2b(T &&obj) { container<2>(std::forward(obj)); } + + template + void container4b(T &&obj) { container<4>(std::forward(obj)); } + + template + void container8b(T &&obj) { container<8>(std::forward(obj)); } + template - void array1(std::array &arr) { array<1>(arr); } + void container1b(T (&arr)[N]) { container<1>(arr); } template - void array2(std::array &arr) { array<2>(arr); } + void container2b(T (&arr)[N]) { container<2>(arr); } template - void array4(std::array &arr) { array<4>(arr); } + void container4b(T (&arr)[N]) { container<4>(arr); } template - void array8(std::array &arr) { array<8>(arr); } - - template - void array1(T (&arr)[N]) { array<1>(arr); } - - template - void array2(T (&arr)[N]) { array<2>(arr); } - - template - void array4(T (&arr)[N]) { array<4>(arr); } - - template - void array8(T (&arr)[N]) { array<8>(arr); } + void container8b(T (&arr)[N]) { container<8>(arr); } private: Reader &_reader; - bool _isValid; + void* _context; void readSize(size_t &size, size_t maxSize) { - size = {}; - if (_isValid) { - unsigned char firstBit; - _isValid = _reader.readBits(firstBit, 1); - if (_isValid) { - if (firstBit) { - _isValid = _reader.readBits(size, 7); - } else { - unsigned char secondBit; - _isValid = _reader.readBits(secondBit, 1); - if (_isValid) { - if (secondBit) { - _isValid = _reader.readBits(size, 14); - } else { - _isValid = _reader.readBits(size, 30); - } - } - } - } - if (_isValid) - _isValid = size <= maxSize; + details::readSize(_reader, size); + if (size > maxSize) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + size = {}; } } @@ -409,7 +386,7 @@ namespace bitsery { //false_type means that we must process all elements individually template void procContainer(It first, It last, std::false_type) { - for (; _isValid && first != last; ++first) + for (; first != last; ++first) value(*first); }; @@ -417,21 +394,21 @@ namespace bitsery { //true_type means, that we can copy whole buffer template void procContainer(It first, It last, std::true_type) { - if (_isValid && first != last) - _isValid = _reader.template readBuffer(&(*first), std::distance(first, last)); + if (first != last) + _reader.template readBuffer(&(*first), std::distance(first, last)); }; //process by calling functions template void procContainer(It first, It last, Fnc fnc) { - for (; _isValid && first != last; ++first) + for (; first != last; ++first) fnc(*this, *first); }; //process object types template void procContainer(It first, It last) { - for (; _isValid && first != last; ++first) + for (; first != last; ++first) object(*first); }; diff --git a/include/bitsery/details/both_common.h b/include/bitsery/details/both_common.h new file mode 100644 index 0000000..08f2234 --- /dev/null +++ b/include/bitsery/details/both_common.h @@ -0,0 +1,73 @@ +//MIT License +// +//Copyright (c) 2017 Mindaugas Vinkelis +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +#ifndef BITSERY_DETAILS_BOTH_COMMON_H +#define BITSERY_DETAILS_BOTH_COMMON_H + +#include +#include + +namespace bitsery { + namespace details { +/* + * size read/write functions + */ + template + void readSize(Reader& r, size_t& size) { + uint8_t hb{}; + r.template readBytes<1>(hb); + if (hb < 0x80u) { + size = hb; + } else { + uint8_t lb{}; + r.template readBytes<1>(lb); + if (hb & 0x40u) { + uint16_t lw{}; + r.template readBytes<2>(lw); + size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw; + } else { + size = ((hb & 0x7Fu) << 8) | lb; + } + } + } + + template + void writeSize(Writter& w, const size_t size) { + if (size < 0x80u) { + w.template writeBytes<1>(static_cast(size)); + } else { + if (size < 0x4000u) { + w.template writeBytes<1>(static_cast((size >> 8) | 0x80u)); + w.template writeBytes<1>(static_cast(size)); + } else { + assert(size < 0x40000000u); + w.template writeBytes<1>(static_cast((size >> 24) | 0xC0u)); + w.template writeBytes<1>(static_cast(size >> 16)); + w.template writeBytes<2>(static_cast(size)); + } + } + } + + } +} + +#endif //BITSERY_DETAILS_BOTH_COMMON_H diff --git a/include/bitsery/details/buffer_common.h b/include/bitsery/details/buffer_common.h index 4ba0bf2..3ea6add 100644 --- a/include/bitsery/details/buffer_common.h +++ b/include/bitsery/details/buffer_common.h @@ -20,15 +20,17 @@ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. -#ifndef BITSERY_BUFFER_COMMON_H -#define BITSERY_BUFFER_COMMON_H +#ifndef BITSERY_DETAILS_BUFFER_COMMON_H +#define BITSERY_DETAILS_BUFFER_COMMON_H #include -#include -#include #include #include #include +#include +#include +#include +#include "both_common.h" namespace bitsery { @@ -47,6 +49,11 @@ namespace bitsery { I end() const { return this->second; } }; + enum class BufferReaderError { + NO_ERROR, + BUFFER_OVERFLOW, + INVALID_BUFFER_DATA + }; namespace details { template @@ -124,36 +131,218 @@ namespace bitsery { using type = uint64_t; }; +// struct BufferSessionInfo { +// size_t depth; +// size_t offset; +// }; + + class BufferSessionsWriter { + public: + void begin() { + //write position + _sessionIndex.push(_sessions.size()); + _sessions.emplace_back(0); + } + void end(size_t pos) { + assert(!_sessionIndex.empty()); + //change position to session end + auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top()); + _sessionIndex.pop(); + *sessionIt = pos; + } + template + void flushSessions(TWriter& writer) { + if (_sessions.size()) { + assert(_sessionIndex.empty()); + auto range = writer.getWrittenRange(); + auto dataSize = static_cast(std::distance(range.begin(), range.end())); + for(auto& s:_sessions) { + details::writeSize(writer, s); + } + _sessions.clear(); + + range = writer.getWrittenRange(); + auto totalSize = static_cast(std::distance(range.begin(), range.end())); + //write offset where actual data ends + auto sessionsOffset = totalSize - dataSize + 2;//2 bytes for offset data + if (sessionsOffset < 0x8000u) { + writer.template writeBytes<2>(static_cast(sessionsOffset)); + } else { + //size doesnt fit in 2 bytes, write 4 bytes instead + sessionsOffset+=2; + uint16_t low = static_cast(sessionsOffset); + //mark most significant bit, that size is 4 bytes + uint16_t high = static_cast(0x8000u | (sessionsOffset >> 16)); + writer.template writeBytes<2>(low); + writer.template writeBytes<2>(high); + } + } + } + private: + std::vector _sessions{}; + std::stack _sessionIndex; + }; + + template + struct BufferSessionsReader { + TIterator _bufBegin; + BufferSessionsReader(TReader& r, TIterator& begin, TIterator& end) + :_reader{r}, + _pos{begin}, + _end{end} + { + _bufBegin = begin; + } + void begin() { + if (_sessions.empty()) + initializeSessions(); + //save end position for current session + _sessionsStack.push(_end); + if (_nextSessionIt != std::end(_sessions)) { + if (std::distance(_pos, _end) > 0) { + //set end position for new session + auto newEnd = std::next(_bufBegin, *_nextSessionIt); + if (std::distance(newEnd, _end) < 0) + { + //new session cannot end further than current end + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + return; + } + _end = newEnd; + ++_nextSessionIt; + } + //if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to + } else { + //there is no data to read anymore + //pos == end or buffer overflow while session is active + if (!(_pos == _end || _reader.getError() == BufferReaderError::NO_ERROR)) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + } + } + } + + void end() { + if (!_sessionsStack.empty()) { + //move position to the end of session + //can additionaly be checked for session data versioning + //_pos == _end : same versions + //distance(_pos,_end) > 0: reading newer version + //getError() == BUFFER_OVERFLOW: reading older version + auto dist = std::distance(_pos, _end); + if (dist > 0) { + //newer version might have some inner sessions, try to find the one after current ends + auto currPos = static_cast(std::distance(_bufBegin, _end)); + for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) { + if (*_nextSessionIt > currPos) + break; + } + } + _pos = _end; + //restore end position + _end = _sessionsStack.top(); + _sessionsStack.pop(); + } + } + + bool hasActiveSessions() const { + return _sessionsStack.size() > 0; + } + + private: + TReader& _reader; + TIterator& _pos; + TIterator& _end; + + std::vector _sessions{}; + std::vector::iterator _nextSessionIt{}; + std::stack _sessionsStack{}; + + void initializeSessions() { + //save current position + auto currPos = _pos; + auto bufferSizeLeft = std::distance(_pos, _end); + //read size + if (bufferSizeLeft < 2) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + return; + } + auto endSessionsSizesIt = std::next(_end, -2); + _pos = endSessionsSizesIt; + size_t sessionsOffset{}; + uint16_t high; + _reader.template readBytes<2>(high); + + + if (high >= 0x8000u) { + if (bufferSizeLeft < 4) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + return; + } + endSessionsSizesIt = std::next(endSessionsSizesIt, -2); + _pos = endSessionsSizesIt; + uint16_t low; + _reader.template readBytes<2>(low); + //mask out last bit + high &= 0x7FFFu; + sessionsOffset = static_cast((high << 16) | low); + + } else + sessionsOffset = high; + if (static_cast(bufferSizeLeft) < sessionsOffset) { + _reader.setError(BufferReaderError::INVALID_BUFFER_DATA); + return; + } + //we can initialy resizes to this value, and we'll shrink it after reading + //read session sizes + auto sessionsIt = std::back_inserter(_sessions); + _pos = std::next(_end, -sessionsOffset); + while (std::distance(_pos, endSessionsSizesIt) > 0) { + //todo try to read into iterator directly + size_t size; + details::readSize(_reader, size); + *sessionsIt++ = size; + } + _sessions.shrink_to_fit(); + //set iterators to data + _pos = currPos; + _end = std::next(_end, -sessionsOffset); + _nextSessionIt = std::begin(_sessions);//set before first session; + } + }; + template class WriteBufferContext { + }; template - class WriteBufferContext { + class WriteBufferContext{ public: using ValueType = typename Buffer::value_type; using IteratorType = typename Buffer::iterator; explicit WriteBufferContext(Buffer &buffer) : _buffer{buffer}, - _outIt{buffer.begin()}, - _end{buffer.end()} + _outIt{std::addressof(*std::begin(buffer))}, + _end{std::addressof(*std::end(buffer))} { } void write(const ValueType *data, size_t size) { assert(std::distance(_outIt, _end) >= static_cast(size)); - _outIt = std::copy_n(data, size, _outIt); + memcpy(_outIt, data, size); + _outIt += size; } BufferRange getWrittenRange() const { - return BufferRange{_buffer.begin(), _outIt}; + auto begin = std::begin(_buffer); + return BufferRange{begin, std::next(begin, _outIt - std::addressof(*begin))}; } private: Buffer &_buffer; - IteratorType _outIt; - IteratorType _end; + ValueType* _outIt; + ValueType* _end; }; template @@ -169,11 +358,12 @@ namespace bitsery { } void write(const ValueType *data, size_t size) { - if (std::distance(_outIt, _end) >= static_cast(size)) { - _outIt = std::copy_n(data, size, _outIt); + if ((_end - _outIt) >= static_cast(size)) { + std::memcpy(_outIt, data, size); + _outIt += size; } else { //get current position before invalidating iterators - auto pos = std::distance(_buffer.begin(), _outIt); + auto pos = std::distance(std::addressof(*std::begin(_buffer)), _outIt); //make dummy call to back insert iterator to resize buffer *(std::back_insert_iterator(_buffer)) = {}; resizeToCapacity(pos); @@ -182,24 +372,26 @@ namespace bitsery { } BufferRange getWrittenRange() const { - return BufferRange{_buffer.begin(), _outIt}; + auto begin = std::begin(_buffer); + return BufferRange{begin, std::next(begin, _outIt - std::addressof(*begin))}; } private: + void resizeToCapacity(typename Buffer::difference_type writePos) { if (_buffer.capacity() != _buffer.size()) { _buffer.resize(_buffer.capacity()); } - _end = _buffer.end(); - _outIt = std::next(_buffer.begin(), writePos); + _end = std::addressof(*std::end(_buffer)); + _outIt = std::addressof(*std::next(std::begin(_buffer), writePos)); } Buffer &_buffer; - IteratorType _outIt; - IteratorType _end; + ValueType* _outIt; + ValueType* _end; }; } } -#endif //BITSERY_BUFFER_COMMON_H +#endif //BITSERY_DETAILS_BUFFER_COMMON_H diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h index b23d98d..c1c3b01 100644 --- a/include/bitsery/details/serialization_common.h +++ b/include/bitsery/details/serialization_common.h @@ -20,12 +20,12 @@ //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. -#ifndef BITSERY_SERIALIZATION_COMMON_H -#define BITSERY_SERIALIZATION_COMMON_H +#ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H +#define BITSERY_DETAILS_SERIALIZATION_COMMON_H -#include #include #include +#include "both_common.h" namespace bitsery { @@ -60,8 +60,20 @@ namespace bitsery { return getSize(max - min, 0); } + template + struct IsResizable : std::false_type {}; + + template + struct IsResizable ().resize(1u), 0)> : std::true_type {}; } + /* + * serialization/deserialization context + */ + struct Context { + void* getCustomPtr(); + }; + /* * range functions in bitsery namespace because these are used by user */ @@ -138,7 +150,8 @@ namespace bitsery { template::value>::type * = nullptr> auto getRangeValue(const T &v, const RangeSpec &r) { - return static_cast>(v) - static_cast>(r.min); + using VT = SAME_SIZE_UNSIGNED; + return static_cast(static_cast(v) - static_cast(r.min)); }; template::value>::type * = nullptr> @@ -181,11 +194,11 @@ namespace bitsery { } /* - * functions for substitution + * functions for entropy encoding */ template - size_t findSubstitutionIndex(const T &v, const std::array &defValues) { + size_t findEntropyIndex(const T &v, const T (&defValues)[N]) { auto index{1u}; for (auto &d:defValues) { if (d == v) @@ -215,6 +228,7 @@ namespace bitsery { } }; + /* * delta functions */ @@ -256,4 +270,4 @@ namespace bitsery { } -#endif //BITSERY_SERIALIZATION_COMMON_H +#endif //BITSERY_DETAILS_SERIALIZATION_COMMON_H diff --git a/include/bitsery/ext/optional.h b/include/bitsery/ext/optional.h index 68fabb8..c2fa1c9 100644 --- a/include/bitsery/ext/optional.h +++ b/include/bitsery/ext/optional.h @@ -48,16 +48,16 @@ namespace bitsery { static_assert(std::is_default_constructible::value, ""); }; - template - void serialize(const T &obj, Ser &ser, Fnc &&fnc) const { + template + void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const { assertType(); ser.boolByte(static_cast(obj)); if (obj) fnc(ser, *obj); } - template - void deserialize(T &obj, Des &des, Fnc &&fnc) const { + template + void deserialize(Des &des, Reader& , T &obj, Fnc &&fnc) const { assertType(); bool exists{}; des.boolByte(exists); diff --git a/include/bitsery/serializer.h b/include/bitsery/serializer.h index 0396e86..fa1655f 100644 --- a/include/bitsery/serializer.h +++ b/include/bitsery/serializer.h @@ -21,7 +21,6 @@ //SOFTWARE. - #ifndef BITSERY_SERIALIZER_H #define BITSERY_SERIALIZER_H @@ -29,14 +28,21 @@ #include "details/serialization_common.h" #include #include -#include namespace bitsery { template class Serializer { public: - Serializer(Writter &w) : _writter{w} {}; + Serializer(Writter &w, void* context = nullptr) : _writter{w}, _context{context} {}; + + /* + * get serialization context. + * this is optional, but might be required for some specific serialization flows. + */ + void* getContext() { + return _context; + } /* * object function @@ -46,6 +52,11 @@ namespace bitsery { details::SerializeFunction::invoke(*this, std::forward(obj)); } + template + void object(T &&obj, Fnc &&fnc) { + fnc(*this, std::forward(obj)); + }; + /* * value overloads */ @@ -67,33 +78,35 @@ namespace bitsery { } /* - * custom function + * growable function */ template - void custom(T &&obj, Fnc &&fnc) { - fnc(*this, std::forward(obj)); + void growable(const T &obj, Fnc &&fnc) { + _writter.beginSession(); + fnc(*this, obj); + _writter.endSession(); }; /* - * extension functions + * extend functions */ template - void extension(const T &obj, Ext &&ext, Fnc &&fnc) { - ext.serialize(obj, *this, std::forward(fnc)); + void extend(const T &obj, Ext &&ext, Fnc &&fnc) { + ext.serialize(*this, _writter, obj, std::forward(fnc)); }; template - void extension(const T &obj, Ext &&ext) { - ext.serialize(obj, *this, [](auto &s, auto &v) { s.template value(v); }); + void extend(const T &obj, Ext &&ext) { + ext.serialize(*this, _writter, obj, [](auto &s, auto &v) { s.template value(v); }); }; template - void extension(const T &obj, Ext &&ext) { - ext.serialize(obj, *this, [](auto &s, auto &v) { s.object(v); }); + void extend(const T &obj, Ext &&ext) { + ext.serialize(*this, _writter, obj, [](auto &s, auto &v) { s.object(v); }); }; /* @@ -120,27 +133,27 @@ namespace bitsery { } /* - * substitution overloads + * entropy overloads */ template - void substitution(const T &v, const std::array &expectedValues, Fnc &&fnc) { - auto index = details::findSubstitutionIndex(v, expectedValues); + void entropy(const T &v, const T (&expectedValues)[N], Fnc &&fnc) { + auto index = details::findEntropyIndex(v, expectedValues); range(index, {{}, N + 1}); if (!index) fnc(*this, v); }; template - void substitution(const T &v, const std::array &expectedValues) { - auto index = details::findSubstitutionIndex(v, expectedValues); + void entropy(const T &v, const T (&expectedValues)[N]) { + auto index = details::findEntropyIndex(v, expectedValues); range(index, {{}, N + 1}); if (!index) value(v); }; template - void substitution(const T &v, const std::array &expectedValues) { - auto index = details::findSubstitutionIndex(v, expectedValues); + void entropy(const T &v, const T (&expectedValues)[N]) { + auto index = details::findEntropyIndex(v, expectedValues); range(index, {{}, N + 1}); if (!index) object(v); @@ -151,11 +164,12 @@ namespace bitsery { */ template - void text(const std::basic_string &str, size_t maxSize) { - assert(str.size() <= maxSize); + void text(const T &str, size_t maxSize) { auto first = std::begin(str); auto last = std::end(str); - writeSize(std::distance(first, last)); + auto size = static_cast(std::distance(first, last)); + assert(size <= maxSize); + writeSize(size); procContainer(first, last, std::true_type{}); } @@ -171,8 +185,12 @@ namespace bitsery { * container overloads */ + //dynamic size containers + template void container(const T &obj, size_t maxSize, Fnc &&fnc) { + static_assert(details::IsResizable::value, + "use container(const T&, Fnc) overload without `maxSize` for static containers"); assert(obj.size() <= maxSize); writeSize(obj.size()); procContainer(std::begin(obj), std::end(obj), std::forward(fnc)); @@ -180,6 +198,8 @@ namespace bitsery { template void container(const T &obj, size_t maxSize) { + static_assert(details::IsResizable::value, + "use container(const T&) overload without `maxSize` for static containers"); static_assert(VSIZE > 0, ""); assert(obj.size() <= maxSize); writeSize(obj.size()); @@ -189,48 +209,53 @@ namespace bitsery { template void container(const T &obj, size_t maxSize) { + static_assert(details::IsResizable::value, + "use container(const T&) overload without `maxSize` for static containers"); assert(obj.size() <= maxSize); writeSize(obj.size()); procContainer(std::begin(obj), std::end(obj)); } - /* - * array overloads (fixed size array (std::array, and c-style array)) - */ + //fixed size containers - //std::array overloads - - template - void array(const std::array &arr, Fnc &&fnc) { - procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); + template::value>::type * = nullptr> + void container(const T &obj, Fnc &&fnc) { + static_assert(!details::IsResizable::value, + "use container(const T&, size_t, Fnc) overload with `maxSize` for dynamic containers"); + procContainer(std::begin(obj), std::end(obj), std::forward(fnc)); } - template - void array(const std::array &arr) { + template + void container(const T &obj) { + static_assert(!details::IsResizable::value, + "use container(const T&, size_t) overload with `maxSize` for dynamic containers"); static_assert(VSIZE > 0, ""); - procContainer(std::begin(arr), std::end(arr), std::true_type{}); + //todo optimisation is possible for contigous containers, but currently there is no compile-time check for this + procContainer(std::begin(obj), std::end(obj), std::false_type{}); } - template - void array(const std::array &arr) { - procContainer(std::begin(arr), std::end(arr)); + template + void container(const T &obj) { + static_assert(!details::IsResizable::value, + "use container(const T&, size_t) overload with `maxSize` for dynamic containers"); + procContainer(std::begin(obj), std::end(obj)); } //c-style array overloads template - void array(const T (&arr)[N], Fnc &&fnc) { + void container(const T (&arr)[N], Fnc &&fnc) { procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); } template - void array(const T (&arr)[N]) { + void container(const T (&arr)[N]) { static_assert(VSIZE > 0, ""); procContainer(std::begin(arr), std::end(arr), std::true_type{}); } template - void array(const T (&arr)[N]) { + void container(const T (&arr)[N]) { procContainer(std::begin(arr), std::end(arr)); } @@ -238,127 +263,112 @@ namespace bitsery { _writter.align(); } - bool isValid() const { - //serialization cannot fail, it doesn't handle out of memory exception - return true; - } - - //overloads for functions with explicit type size template - void value1(T &&v) { value<1>(std::forward(v)); } + void value1b(T &&v) { value<1>(std::forward(v)); } template - void value2(T &&v) { value<2>(std::forward(v)); } + void value2b(T &&v) { value<2>(std::forward(v)); } template - void value4(T &&v) { value<4>(std::forward(v)); } + void value4b(T &&v) { value<4>(std::forward(v)); } template - void value8(T &&v) { value<8>(std::forward(v)); } + void value8b(T &&v) { value<8>(std::forward(v)); } template - void extension1(const T &v, Ext &&ext) { extension<1>(v, std::forward(ext)); }; + void extend1b(const T &v, Ext &&ext) { extend<1>(v, std::forward(ext)); }; template - void extension2(const T &v, Ext &&ext) { extension<2>(v, std::forward(ext)); }; + void extend2b(const T &v, Ext &&ext) { extend<2>(v, std::forward(ext)); }; template - void extension4(const T &v, Ext &&ext) { extension<4>(v, std::forward(ext)); }; + void extend4b(const T &v, Ext &&ext) { extend<4>(v, std::forward(ext)); }; template - void extension8(const T &v, Ext &&ext) { extension<8>(v, std::forward(ext)); }; + void extend8b(const T &v, Ext &&ext) { extend<8>(v, std::forward(ext)); }; template - void substitution1(const T &v, const std::array &expectedValues) { - substitution<1>(v, expectedValues); + void entropy1b(const T &v, const T (&expectedValues)[N]) { + entropy<1>(v, expectedValues); }; template - void substitution2(const T &v, const std::array &expectedValues) { - substitution<2>(v, expectedValues); + void entropy2b(const T &v, const T (&expectedValues)[N]) { + entropy<2>(v, expectedValues); }; template - void substitution4(const T &v, const std::array &expectedValues) { - substitution<4>(v, expectedValues); + void entropy4b(const T &v, const T (&expectedValues)[N]) { + entropy<4>(v, expectedValues); }; template - void substitution8(const T &v, const std::array &expectedValues) { - substitution<8>(v, expectedValues); + void entropy8b(const T &v, const T (&expectedValues)[N]) { + entropy<8>(v, expectedValues); }; template - void text1(const std::basic_string &str, size_t maxSize) { text<1>(str, maxSize); } + void text1b(const T &str, size_t maxSize) { text<1>(str, maxSize); } template - void text2(const std::basic_string &str, size_t maxSize) { text<2>(str, maxSize); } + void text2b(const T &str, size_t maxSize) { text<2>(str, maxSize); } template - void text4(const std::basic_string &str, size_t maxSize) { text<4>(str, maxSize); } + void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); } template - void text1(const T (&str)[N]) { text<1>(str); } + void text1b(const T (&str)[N]) { text<1>(str); } template - void text2(const T (&str)[N]) { text<2>(str); } + void text2b(const T (&str)[N]) { text<2>(str); } template - void text4(const T (&str)[N]) { text<4>(str); } + void text4b(const T (&str)[N]) { text<4>(str); } template - void container1(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } + void container1b(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } template - void container2(T &&obj, size_t maxSize) { container<2>(std::forward(obj), maxSize); } + void container2b(T &&obj, size_t maxSize) { container<2>(std::forward(obj), maxSize); } template - void container4(T &&obj, size_t maxSize) { container<4>(std::forward(obj), maxSize); } + void container4b(T &&obj, size_t maxSize) { container<4>(std::forward(obj), maxSize); } template - void container8(T &&obj, size_t maxSize) { container<8>(std::forward(obj), maxSize); } + void container8b(T &&obj, size_t maxSize) { container<8>(std::forward(obj), maxSize); } + + template + void container1b(T &&obj) { container<1>(std::forward(obj)); } + + template + void container2b(T &&obj) { container<2>(std::forward(obj)); } + + template + void container4b(T &&obj) { container<4>(std::forward(obj)); } + + template + void container8b(T &&obj) { container<8>(std::forward(obj)); } template - void array1(const std::array &arr) { array<1>(arr); } + void container1b(const T (&arr)[N]) { container<1>(arr); } template - void array2(const std::array &arr) { array<2>(arr); } + void container2b(const T (&arr)[N]) { container<2>(arr); } template - void array4(const std::array &arr) { array<4>(arr); } + void container4b(const T (&arr)[N]) { container<4>(arr); } template - void array8(const std::array &arr) { array<8>(arr); } - - template - void array1(const T (&arr)[N]) { array<1>(arr); } - - template - void array2(const T (&arr)[N]) { array<2>(arr); } - - template - void array4(const T (&arr)[N]) { array<4>(arr); } - - template - void array8(const T (&arr)[N]) { array<8>(arr); } + void container8b(const T (&arr)[N]) { container<8>(arr); } private: Writter &_writter; + void* _context; void writeSize(const size_t size) { - if (size < 0x80u) { - _writter.writeBits(1u, 1); - _writter.writeBits(size, 7); - } else if (size < 0x4000u) { - _writter.writeBits(2u, 2); - _writter.writeBits(size, 14); - } else { - assert(size < 0x40000000u); - _writter.writeBits(0u, 2); - _writter.writeBits(size, 30); - } + details::writeSize(_writter, size); } //process value types diff --git a/tests/buffer_operations.cpp b/tests/buffer_operations.cpp index 67f11b4..6cfb4f5 100644 --- a/tests/buffer_operations.cpp +++ b/tests/buffer_operations.cpp @@ -109,19 +109,24 @@ TEST(BufferBitsAndBytesOperations, BufferSizeIsCountedPerByteNotPerBit) { //read from buffer BufferReader br{range}; uint16_t tmp; - EXPECT_THAT(br.readBits(tmp,4), Eq(true)); - EXPECT_THAT(br.readBits(tmp,2), Eq(true)); - EXPECT_THAT(br.readBits(tmp,2), Eq(true)); - EXPECT_THAT(br.readBits(tmp,2), Eq(false)); + br.readBits(tmp,4); + br.readBits(tmp,2); + br.readBits(tmp,2); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + br.readBits(tmp,2); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false //part of next byte BufferReader br1{range}; - EXPECT_THAT(br1.readBits(tmp,2), Eq(true)); - EXPECT_THAT(br1.readBits(tmp,7), Eq(false)); + br1.readBits(tmp,2); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + br1.readBits(tmp,7); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false //bigger than byte BufferReader br2{range}; - EXPECT_THAT(br2.readBits(tmp,9), Eq(false)); + br2.readBits(tmp,9); + EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false } TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) { @@ -141,18 +146,20 @@ TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) { unsigned char tmp; BufferReader br{bw.getWrittenRange()}; - EXPECT_THAT(br.readBits(tmp,2), Eq(true)); + br.readBits(tmp,2); EXPECT_THAT(tmp, Eq(3u)); - EXPECT_THAT(br.align(), Eq(true)); - - EXPECT_THAT(br.readBits(tmp,3), Eq(true)); + br.align(); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + br.readBits(tmp,3); + br.align(); + br.align(); + br.align(); EXPECT_THAT(tmp, Eq(7u)); - EXPECT_THAT(br.align(), Eq(true)); - EXPECT_THAT(br.align(), Eq(true)); - EXPECT_THAT(br.align(), Eq(true)); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); - EXPECT_THAT(br.readBits(tmp,4), Eq(true)); + br.readBits(tmp,4); EXPECT_THAT(tmp, Eq(15u)); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); } TEST(BufferBitsAndBytesOperations, AlignWritesZerosBits) { @@ -166,18 +173,20 @@ TEST(BufferBitsAndBytesOperations, AlignWritesZerosBits) { bw.writeBits(3u, 2); bw.align(); bw.flush(); - + auto range = bw.getWrittenRange(); + EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1)); unsigned char tmp; - BufferReader br1{bw.getWrittenRange()}; + BufferReader br1{range}; br1.readBits(tmp,2); //read aligned bits - EXPECT_THAT(br1.readBits(tmp,6), Eq(true)); + br1.readBits(tmp,6); EXPECT_THAT(tmp, Eq(0)); - BufferReader br2{bw.getWrittenRange()}; + BufferReader br2{range}; //read 2 bits br2.readBits(tmp,2); - EXPECT_THAT(br2.align(), Eq(true)); + br2.align(); + EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); } @@ -219,12 +228,13 @@ TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) { //read from buffer BufferReader br{range}; IntegralTypes res{}; - EXPECT_THAT(br.readBytes<4>(res.b), Eq(true)); - EXPECT_THAT(br.readBytes<2>(res.c), Eq(true)); - EXPECT_THAT(br.readBytes<1>(res.d), Eq(true)); - EXPECT_THAT(br.readBytes<8>(res.a), Eq(true)); - EXPECT_THAT(br.readBytes<1>(res.e), Eq(true)); - EXPECT_THAT(br.readBuffer<1>(res.f, 2), Eq(true)); + br.readBytes<4>(res.b); + br.readBytes<2>(res.c); + br.readBytes<1>(res.d); + br.readBytes<8>(res.a); + br.readBytes<1>(res.e); + br.readBuffer<1>(res.f, 2); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); //assert results EXPECT_THAT(data.a, Eq(res.a)); @@ -248,13 +258,9 @@ TEST(BufferBitsAndBytesOperations, ReadWriteBufferFncCanAcceptSignedData) { //read from buffer BufferReader br1{bw.getWrittenRange()}; int16_t dst[DATA_SIZE]{}; - EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true)); + br1.readBuffer<2>(dst, DATA_SIZE); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); EXPECT_THAT(dst, ContainerEq(src)); - - //read more than available - BufferReader br2{bw.getWrittenRange()}; - int16_t dstMore[DATA_SIZE+1]{}; - EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false)); } TEST(BufferBitsAndBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) { @@ -277,16 +283,37 @@ TEST(BufferBitsAndBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) { uint8_t tmp{}; br1.readBits(tmp, 4); EXPECT_THAT(tmp, Eq(15)); - EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true)); + br1.readBuffer<2>(dst, DATA_SIZE); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); EXPECT_THAT(dst, ContainerEq(src)); br1.readBits(tmp, 4); EXPECT_THAT(tmp, Eq(12)); - - //read more than available - BufferReader br2{range}; - br2.readBits(tmp, 4); - int16_t dstMore[DATA_SIZE+1]{}; - EXPECT_THAT(tmp, Eq(15)); - EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false)); } +TEST(BufferBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) { + //setup data + int16_t data[2]{0x0000, 0x7FFF}; + int16_t res[2]{}; + //create and write to buffer + Buffer buf{}; + BufferWriter bw{buf}; + bw.writeBits(2u, 2); + bw.writeBytes<2>(data[0]); + bw.writeBytes<2>(data[1]); + bw.align(); + bw.flush(); + auto range = bw.getWrittenRange(); + + //read from buffer + BufferReader br{range}; + uint8_t tmp{}; + br.readBits(tmp, 2); + EXPECT_THAT(tmp, Eq(2)); + br.readBytes<2>(res[0]); + br.readBytes<2>(res[1]); + br.align(); + EXPECT_THAT(res[0], Eq(data[0])); + EXPECT_THAT(res[1], Eq(data[1])); +} + + diff --git a/tests/buffer_reading.cpp b/tests/buffer_reading.cpp index 512da1e..51106d9 100644 --- a/tests/buffer_reading.cpp +++ b/tests/buffer_reading.cpp @@ -28,7 +28,6 @@ #include using testing::Eq; -using testing::ContainerEq; using bitsery::BufferWriter; using bitsery::BufferReader; using Buffer = bitsery::DefaultConfig::BufferType; @@ -42,7 +41,7 @@ struct IntegralTypes { int8_t f[2]; }; -TEST(BufferReading, ReadReturnsFalseIfNotEnoughBufferSize) { +TEST(BufferReading, WhenReadingMoreThanAvailableThenEmptyBufferError) { //setup data uint8_t a = 111; @@ -56,19 +55,34 @@ TEST(BufferReading, ReadReturnsFalseIfNotEnoughBufferSize) { bw.flush(); //read from buffer BufferReader br{bw.getWrittenRange()}; - int16_t b; int32_t c; - EXPECT_THAT(br.readBytes<4>(c), Eq(false)); - EXPECT_THAT(br.readBytes<2>(b), Eq(true)); - EXPECT_THAT(br.readBytes<2>(b), Eq(false)); - EXPECT_THAT(br.readBytes<1>(a), Eq(true)); - EXPECT_THAT(br.readBytes<1>(a), Eq(false)); - EXPECT_THAT(br.readBytes<2>(b), Eq(false)); - EXPECT_THAT(br.readBytes<4>(c), Eq(false)); - + br.readBytes<4>(c); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); } -TEST(BufferReading, ReadIsCompletedWhenAllBytesAreRead) { +TEST(BufferReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) { + //setup data + uint8_t a = 111; + + //create and write to buffer + Buffer buf{}; + BufferWriter bw{buf}; + + bw.writeBytes<1>(a); + bw.writeBytes<1>(a); + bw.writeBytes<1>(a); + bw.flush(); + //read from buffer + BufferReader br{bw.getWrittenRange()}; + int32_t c; + br.readBytes<4>(c); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); + br.readBytes<1>(a); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); +} + + +TEST(BufferReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) { //setup data IntegralTypes data; data.b = 94545646; @@ -86,22 +100,59 @@ TEST(BufferReading, ReadIsCompletedWhenAllBytesAreRead) { //read from buffer BufferReader br{bw.getWrittenRange()}; IntegralTypes res; - EXPECT_THAT(br.readBytes<4>(res.b), Eq(true)); - EXPECT_THAT(br.readBytes<2>(res.c), Eq(true)); - EXPECT_THAT(br.isCompleted(), Eq(false)); - EXPECT_THAT(br.readBytes<1>(res.d), Eq(true)); - EXPECT_THAT(br.isCompleted(), Eq(true)); - EXPECT_THAT(br.readBytes<1>(res.d), Eq(false)); - EXPECT_THAT(br.isCompleted(), Eq(true)); + br.readBytes<4>(res.b); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + br.readBytes<2>(res.c); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false)); + br.readBytes<1>(res.d); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true)); + br.readBytes<1>(res.d); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); + EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false)); BufferReader br1{bw.getWrittenRange()}; - EXPECT_THAT(br1.readBytes<4>(res.b), Eq(true)); - EXPECT_THAT(br1.readBytes<2>(res.c), Eq(true)); - EXPECT_THAT(br1.isCompleted(), Eq(false)); - EXPECT_THAT(br1.readBytes<2>(res.c), Eq(false)); - EXPECT_THAT(br1.isCompleted(), Eq(false)); - EXPECT_THAT(br1.readBytes<1>(res.d), Eq(true)); - EXPECT_THAT(br1.isCompleted(), Eq(true)); - + br1.readBytes<4>(res.b); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + br1.readBytes<2>(res.c); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false)); + br1.readBytes<2>(res.c); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); + EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false)); + br1.readBytes<1>(res.d); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); + EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false)); } +TEST(BufferReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) { + //setup data + uint8_t a = 111; + + //create and write to buffer + Buffer buf{}; + BufferWriter bw{buf}; + + bw.writeBytes<1>(a); + bw.writeBytes<1>(a); + bw.writeBytes<1>(a); + bw.flush(); + //read from buffer + BufferReader br{bw.getWrittenRange()}; + int32_t c; + br.readBytes<4>(c); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); + + int16_t r1= {-645}; + uint32_t r2[2] = {54898,87854}; + uint8_t r3 = 0xFF; + + br.readBytes<2>(r1); + br.readBuffer<4>(r2, 2); + br.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)); +} diff --git a/tests/buffer_reading_errors.cpp b/tests/buffer_reading_errors.cpp new file mode 100644 index 0000000..e1dfc50 --- /dev/null +++ b/tests/buffer_reading_errors.cpp @@ -0,0 +1,119 @@ +//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" + +using testing::Eq; +using bitsery::BufferWriter; +using bitsery::BufferReader; +using Buffer = bitsery::DefaultConfig::BufferType; + +TEST(BufferReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidBufferDataError) { + SerializationContext ctx; + std::string tmp = "larger text then allowed"; + ctx.createSerializer().text1b(tmp,100); + ctx.createDeserializer().text1b(tmp, 10); + EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} + +TEST(BufferReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) { + SerializationContext ctx; + auto ser = ctx.createSerializer(); + ser.value1b(uint8_t{1}); + ser.value1b(uint8_t{2}); + bool res{}; + auto des = ctx.createDeserializer(); + des.boolByte(res); + EXPECT_THAT(res, Eq(true)); + des.boolByte(res); + EXPECT_THAT(res, Eq(false)); + EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} + +TEST(BufferReadingErrors, WhenReadingAlignHasNonZerosThenInvalidBufferDataError) { + Buffer buf{}; + BufferWriter bw{buf}; + uint8_t tmp{0xFF}; + bw.writeBytes<1>(tmp); + bw.flush(); + BufferReader br{bw.getWrittenRange()}; + + br.readBits(tmp,3); + br.align(); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} + +TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBufferError) { + uint8_t tmp{0xFF}; + Buffer buf{}; + BufferWriter bw{buf}; + for (auto i = 0; i < 2; ++i) { + bw.beginSession(); + bw.writeBytes<1>(tmp); + bw.writeBytes<1>(tmp); + bw.endSession(); + } + bw.flush(); + BufferReader br{bw.getWrittenRange()}; + 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.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} + + +TEST(BufferReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidBufferData) { + uint8_t tmp1{0xFF}; + Buffer buf1{}; + BufferWriter bw1{buf1}; + bw1.writeBytes<1>(tmp1); + bw1.flush(); + BufferReader br1{bw1.getWrittenRange()}; + br1.beginSession(); + EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); + + Buffer buf2{}; + BufferWriter bw2{buf2}; + uint16_t tmp2{0x8000}; + bw2.writeBytes<2>(tmp2); + bw2.flush(); + BufferReader br2{bw2.getWrittenRange()}; + br2.beginSession(); + EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} + +TEST(BufferReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidBufferData) { + Buffer buf{}; + BufferWriter bw{buf}; + bw.writeBytes<1>(uint8_t{1}); + bw.writeBytes<1>(uint8_t{1}); + bw.writeBytes<2>(uint16_t{10}); + BufferReader br{bw.getWrittenRange()}; + br.beginSession(); + EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA)); +} diff --git a/tests/buffer_writing.cpp b/tests/buffer_writing.cpp index dfac540..7b71270 100644 --- a/tests/buffer_writing.cpp +++ b/tests/buffer_writing.cpp @@ -81,9 +81,10 @@ TYPED_TEST(BufferWriting, GetWrittenRangeReturnsBeginEndIterators) { } TYPED_TEST(BufferWriting, WhenWritingBitsThenMustFlushWriter) { - Buffer buf; - bitsery::BufferWriter bw{buf}; - + using Config = typename TestFixture::type; + using Buffer = typename Config::BufferType; + Buffer buf{}; + bitsery::BasicBufferWriter bw{buf}; bw.writeBits(3u, 2); auto range1 = bw.getWrittenRange(); bw.flush(); diff --git a/tests/serialization_container.cpp b/tests/serialization_container.cpp index e46ed8d..c15c1d0 100644 --- a/tests/serialization_container.cpp +++ b/tests/serialization_container.cpp @@ -24,6 +24,7 @@ #include #include "serialization_test_utils.h" +#include #include #include #include @@ -36,29 +37,29 @@ using testing::Eq; * overload to get container of types */ -template +template Container getFilledContainer() { - return {1,2,3,4,5,78,456,8,54}; + return {1, 2, 3, 4, 5, 78, 456, 8, 54}; } -template <> +template<> std::vector getFilledContainer>() { return { - {0,1}, - {2,3}, - {4,5}, - {6,7}, - {8,9}, - {11,34}, - {5134,1532} + {0, 1}, + {2, 3}, + {4, 5}, + {6, 7}, + {8, 9}, + {11, 34}, + {5134, 1532} }; } -template <> +template<> std::list getFilledContainer>() { return { - {MyStruct2::V1, {0,1}} , - {MyStruct2::V3, {-45,45}} + {MyStruct2::V1, {0, 1}}, + {MyStruct2::V3, {-45, 45}} }; } @@ -66,16 +67,16 @@ std::list getFilledContainer>() { * start testing session */ -template -class SerializeContainerArthmeticTypes:public testing::Test { +template +class SerializeContainerDynamicSizeArthmeticTypes : public testing::Test { public: using TContainer = T; using TValue = typename T::value_type; - const TContainer src= getFilledContainer() ; + const TContainer src = getFilledContainer(); TContainer res{}; - size_t getExpectedBufSize(const SerializationContext& ctx) const { + size_t getExpectedBufSize(const SerializationContext &ctx) const { return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * sizeof(TValue); } }; @@ -85,9 +86,9 @@ using SequenceContainersWithArthmeticTypes = ::testing::Types< std::list, std::deque>; -TYPED_TEST_CASE(SerializeContainerArthmeticTypes, SequenceContainersWithArthmeticTypes); +TYPED_TEST_CASE(SerializeContainerDynamicSizeArthmeticTypes, SequenceContainersWithArthmeticTypes); -TYPED_TEST(SerializeContainerArthmeticTypes, Values) { +TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, Values) { SerializationContext ctx{}; using TValue = typename TestFixture::TValue; @@ -98,7 +99,7 @@ TYPED_TEST(SerializeContainerArthmeticTypes, Values) { EXPECT_THAT(this->res, ContainerEq(this->src)); } -TYPED_TEST(SerializeContainerArthmeticTypes, CustomFunctionIncrements) { +TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements) { SerializationContext ctx{}; auto ser = ctx.createSerializer(); @@ -108,13 +109,13 @@ TYPED_TEST(SerializeContainerArthmeticTypes, CustomFunctionIncrements) { s.template value(v); }); auto des = ctx.createDeserializer(); - des.container(this->res, 1000, [](auto &s, auto&v ) { + des.container(this->res, 1000, [](auto &s, auto &v) { s.template value(v); //increment by 1 after reading v++; }); //decrement result by 2, before comparing for eq - for(auto& v:this->res) + for (auto &v:this->res) v -= 2; EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx))); @@ -122,29 +123,28 @@ TYPED_TEST(SerializeContainerArthmeticTypes, CustomFunctionIncrements) { } - - -template -class SerializeContainerCompositeTypes:public testing::Test { +template +class SerializeContainerDynamicSizeCompositeTypes : public testing::Test { public: using TContainer = T; using TValue = typename T::value_type; - const TContainer src= getFilledContainer(); + const TContainer src = getFilledContainer(); TContainer res{}; - size_t getExpectedBufSize(const SerializationContext& ctx) const { + + size_t getExpectedBufSize(const SerializationContext &ctx) const { return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * TValue::SIZE; } }; -using SequenceContainersWithCompositeTypes = ::testing::Types< +using SerializeContainerDynamicSizeWithCompositeTypes = ::testing::Types< std::vector, std::list>; -TYPED_TEST_CASE(SerializeContainerCompositeTypes, SequenceContainersWithCompositeTypes); +TYPED_TEST_CASE(SerializeContainerDynamicSizeCompositeTypes, SerializeContainerDynamicSizeWithCompositeTypes); -TYPED_TEST(SerializeContainerCompositeTypes, DefaultSerializeFunction) { +TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, DefaultSerializeFunction) { SerializationContext ctx{}; ctx.createSerializer().container(this->src, 1000); @@ -155,15 +155,95 @@ TYPED_TEST(SerializeContainerCompositeTypes, DefaultSerializeFunction) { } -TYPED_TEST(SerializeContainerCompositeTypes, CustomFunctionThatDoNothing) { +TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, CustomFunctionThatDoNothing) { SerializationContext ctx{}; - auto emptyFnc = [](auto& s, auto& v) {}; + auto emptyFnc = [](auto &s, auto &v) {}; ctx.createSerializer().container(this->src, 1000, emptyFnc); ctx.createDeserializer().container(this->res, 1000, emptyFnc); EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(this->src.size()))); } +template +class SerializeContainerFixedSizeArithmeticTypes : public testing::Test { +public: + using TContainer = T; + + size_t getContainerSize() { + T tmp{}; + return static_cast(std::distance(std::begin(tmp), std::end(tmp))); + } +}; + +using StaticContainersWithIntegralTypes = ::testing::Types< + std::array, + int16_t[4]>; + +TYPED_TEST_CASE(SerializeContainerFixedSizeArithmeticTypes, StaticContainersWithIntegralTypes); + +TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues) { + using Container = typename TestFixture::TContainer; + Container src{5, 9, 15, -459}; + Container res{}; + + SerializationContext ctx; + ctx.createSerializer().container<2>(src); + ctx.createDeserializer().container<2>(res); + + EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * 2)); + EXPECT_THAT(res, ContainerEq(src)); + +} + + +template +class SerializeContainerFixedSizeCompositeTypes : public SerializeContainerFixedSizeArithmeticTypes { + +}; + +using StaticContainersWithCompositeTypes = ::testing::Types< + std::array, + MyStruct1[4]>; + +TYPED_TEST_CASE(SerializeContainerFixedSizeCompositeTypes, StaticContainersWithCompositeTypes); + +TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, DefaultSerializationFunction) { + using Container = typename TestFixture::TContainer; + Container src{MyStruct1{0, 1}, MyStruct1{8, 9}, MyStruct1{11, 34}, MyStruct1{5134, 1532}}; + Container res{}; + + SerializationContext ctx; + ctx.createSerializer().container(src); + ctx.createDeserializer().container(res); + + EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * MyStruct1::SIZE)); + EXPECT_THAT(res, ContainerEq(src)); +} + +TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializesAnEmptyByteEveryElement) { + using Container = typename TestFixture::TContainer; + Container src{MyStruct1{0, 1}, MyStruct1{2, 3}, MyStruct1{4, 5}, MyStruct1{5134, 1532}}; + Container res{}; + + SerializationContext ctx; + auto ser = ctx.createSerializer(); + ser.container(src, [](auto &s, auto &v) { + char tmp{}; + s.object(v); + s.value1b(tmp); + }); + auto des = ctx.createDeserializer(); + des.container(res, [](auto &s, auto &v) { + char tmp{}; + s.object(v); + s.value1b(tmp); + }); + + EXPECT_THAT(ctx.getBufferSize(), Eq(this->getContainerSize() * (MyStruct1::SIZE + sizeof(char)))); + EXPECT_THAT(res, ContainerEq(src)); +} + + diff --git a/tests/serialization_substitution.cpp b/tests/serialization_entropy.cpp similarity index 67% rename from tests/serialization_substitution.cpp rename to tests/serialization_entropy.cpp index 6a78533..293bda5 100644 --- a/tests/serialization_substitution.cpp +++ b/tests/serialization_entropy.cpp @@ -25,72 +25,72 @@ #include "serialization_test_utils.h" using namespace testing; -TEST(SerializeSubstitution, WhenSubstitutedThenOnlyWriteIndexUsingMinRequiredBits) { +TEST(SerializeEntropyEncoding, WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequiredBits) { int32_t v = 4849; int32_t res; constexpr size_t N = 3; - std::array subsitution{485,4849,89}; + int32_t entropyValues[3]{485,4849,89}; SerializationContext ctx; - ctx.createSerializer().substitution<4>(v, subsitution); - ctx.createDeserializer().substitution<4>(res, subsitution); + ctx.createSerializer().entropy<4>(v, entropyValues); + ctx.createDeserializer().entropy<4>(res, entropyValues); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq(1)); SerializationContext ctx1; - ctx1.createSerializer().substitution<4>(v, subsitution); + ctx1.createSerializer().entropy<4>(v, entropyValues); auto des = ctx1.createDeserializer(); des.range(res, {0, N + 1}); EXPECT_THAT(res, Eq(2)); } -TEST(SerializeSubstitution, WhenNoSubstitutionThenWriteZeroBitsAndValueOrObject) { +TEST(SerializeEntropyEncoding, WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrObject) { int16_t v = 8945; int16_t res; - std::array subsitution{485,4849,89}; + int16_t entropyValues[3]{485,4849,89}; SerializationContext ctx; - ctx.createSerializer().substitution<2>(v, subsitution); - ctx.createDeserializer().substitution<2>(res, subsitution); + ctx.createSerializer().entropy<2>(v, entropyValues); + ctx.createDeserializer().entropy<2>(res, entropyValues); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq(sizeof(int16_t)+1)); } -TEST(SerializeSubstitution, CustomTypeSubstituted) { +TEST(SerializeEntropyEncoding, CustomTypeEntropyEncoded) { MyStruct1 v = {12,10}; MyStruct1 res; constexpr size_t N = 4; - std::array subsitution = { + MyStruct1 entropyValues[N]{ MyStruct1{12,10}, MyStruct1{485, 454}, MyStruct1{4849,89}, MyStruct1{0,1}}; SerializationContext ctx; - ctx.createSerializer().substitution(v, subsitution); - ctx.createDeserializer().substitution(res, subsitution); + ctx.createSerializer().entropy(v, entropyValues); + ctx.createDeserializer().entropy(res, entropyValues); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq(1)); } -TEST(SerializeSubstitution, CustomTypeNotSubstituted) { +TEST(SerializeEntropyEncoding, CustomTypeNotEntropyEncoded) { MyStruct1 v = {8945,4456}; MyStruct1 res; constexpr size_t N = 4; - std::array subsitution = { + MyStruct1 entropyValues[N] { MyStruct1{12,10}, MyStruct1{485, 454}, MyStruct1{4849,89}, MyStruct1{0,1}}; SerializationContext ctx; - ctx.createSerializer().substitution(v, subsitution); - ctx.createDeserializer().substitution(res, subsitution); + ctx.createSerializer().entropy(v, entropyValues); + ctx.createDeserializer().entropy(res, entropyValues); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq(MyStruct1::SIZE + 1)); } -TEST(SerializeSubstitution, CustomFunctionNotSubstituted) { +TEST(SerializeEntropyEncoding, CustomFunctionNotEntropyEncoded) { MyStruct1 v = {8945,4456}; MyStruct1 res; constexpr size_t N = 4; - std::array subsitution = { + MyStruct1 entropyValues[N] { MyStruct1{12,10}, MyStruct1{485, 454}, MyStruct1{4849,89}, MyStruct1{0,1}}; @@ -105,30 +105,30 @@ TEST(SerializeSubstitution, CustomFunctionNotSubstituted) { s.range(v.i1, rangeForValue); s.range(v.i2, rangeForValue); }; - ser.substitution(v, subsitution, serLambda); + ser.entropy(v, entropyValues, serLambda); auto des = ctx.createDeserializer(); auto desLambda = [rangeForValue](auto& s, MyStruct1& v) { s.range(v.i1, rangeForValue); s.range(v.i2, rangeForValue); }; - des.substitution(res, subsitution, desLambda); + des.entropy(res, entropyValues, desLambda); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq((rangeForIndex.bitsRequired + rangeForValue.bitsRequired * 2 - 1) / 8 + 1 )); } -TEST(SerializeSubstitution, WhenSubstitutedThenCustomFunctionNotInvoked) { +TEST(SerializeEntropyEncoding, WhenEntropyEncodedThenCustomFunctionNotInvoked) { MyStruct1 v = {4849,89}; MyStruct1 res; constexpr size_t N = 4; - std::array subsitution = { + MyStruct1 entropyValues[N] { MyStruct1{12,10}, MyStruct1{485, 454}, MyStruct1{4849,89}, MyStruct1{0,1}}; SerializationContext ctx; - ctx.createSerializer().substitution(v, subsitution, [](bitsery::Serializer& ,const MyStruct1& ) {}); - ctx.createDeserializer().substitution(res, subsitution, [](bitsery::Deserializer&, MyStruct1& ) {}); + ctx.createSerializer().entropy(v, entropyValues, [](bitsery::Serializer& ,const MyStruct1& ) {}); + ctx.createDeserializer().entropy(res, entropyValues, [](bitsery::Deserializer&, MyStruct1& ) {}); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(ctx.getBufferSize(), Eq(1)); diff --git a/tests/serialization_ext_optional.cpp b/tests/serialization_ext_optional.cpp index 05bbb2e..f166b77 100644 --- a/tests/serialization_ext_optional.cpp +++ b/tests/serialization_ext_optional.cpp @@ -43,8 +43,8 @@ using testing::Eq; template void test(SerializationContext& ctx, const T& v, T& r) { - ctx.createSerializer().extension4(v, extoptional{}); - ctx.createDeserializer().extension4(r, extoptional{}); + ctx.createSerializer().extend4b(v, extoptional{}); + ctx.createDeserializer().extend4b(r, extoptional{}); } TEST(SerializeExtensionOptional, EmptyOptional) { diff --git a/tests/serialization_fixed_size_array.cpp b/tests/serialization_fixed_size_array.cpp deleted file mode 100644 index 1b836e9..0000000 --- a/tests/serialization_fixed_size_array.cpp +++ /dev/null @@ -1,135 +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 - -using testing::ContainerEq; -using testing::Eq; - -TEST(SerializeFSArrayStdArray, ArithmeticValues) { - SerializationContext ctx; - std::array src{5,9,15,-459}; - std::array res{}; - - ctx.createSerializer().array(src); - ctx.createDeserializer().array(res); - - EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * sizeof(int))); - EXPECT_THAT(res, ContainerEq(src)); - -} - -TEST(SerializeFSArrayStdArray, CompositeTypes) { - SerializationContext ctx; - std::array src{ - MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, - MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; - std::array res{}; - - ctx.createSerializer().array(src); - ctx.createDeserializer().array(res); - - EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * MyStruct1::SIZE)); - EXPECT_THAT(res, ContainerEq(src)); -} -// -// -TEST(SerializeFSArrayStdArray, CustomFunctionThatSerializesAnEmptyByteEveryElement) { - SerializationContext ctx; - std::array src{ - MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, - MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; - std::array res{}; - - - auto ser = ctx.createSerializer(); - ser.array(src, [](auto &s, auto& v) { - char tmp{}; - s.object(v); - s.value1(tmp); - }); - auto des = ctx.createDeserializer(); - des.array(res, [](auto &s, auto& v) { - char tmp{}; - s.object(v); - s.value1(tmp); - }); - - EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * (MyStruct1::SIZE + sizeof(char)))); - EXPECT_THAT(res, ContainerEq(src)); -} - -TEST(SerializeFSArrayCArray, ArithmeticValuesSettingValueSizeExplicitly) { - SerializationContext ctx; - int src[4]{5,9,15,-459}; - int res[4]{}; - - ctx.createSerializer().array(src); - ctx.createDeserializer().array(res); - - EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * sizeof(int))); - EXPECT_THAT(res, ContainerEq(src)); - -} - -TEST(SerializeFSArrayCArray, CompositeTypes) { - SerializationContext ctx; - MyStruct1 src[]{ - MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, - MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; - MyStruct1 res[7]{}; - - ctx.createSerializer().array(src); - ctx.createDeserializer().array(res); - - EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * MyStruct1::SIZE)); - EXPECT_THAT(res, ContainerEq(src)); -} -// -// -TEST(SerializeFSArrayCArray, CustomFunctionThatSerializesAnEmptyByteEveryElement) { - SerializationContext ctx; - MyStruct1 src[]{ - MyStruct1{0,1}, MyStruct1{2,3}, MyStruct1{4,5}, MyStruct1{6,7}, - MyStruct1{8,9}, MyStruct1{11,34}, MyStruct1{5134,1532}}; - MyStruct1 res[7]{}; - - - auto ser = ctx.createSerializer(); - ser.array(src, [](auto& s, auto& v) { - char tmp{}; - s.object(v); - s.value1(tmp); - }); - auto des = ctx.createDeserializer(); - des.array(res, [](auto& s, auto& v) { - char tmp{}; - s.object(v); - s.value1(tmp); - }); - - EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent::value * (MyStruct1::SIZE + sizeof(char)))); - EXPECT_THAT(res, ContainerEq(src)); -} diff --git a/tests/serialization_growable.cpp b/tests/serialization_growable.cpp new file mode 100644 index 0000000..1e3e7c0 --- /dev/null +++ b/tests/serialization_growable.cpp @@ -0,0 +1,412 @@ +//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" + +using namespace testing; + +using Buffer = typename bitsery::DefaultConfig::BufferType; +using DiffType = typename Buffer::difference_type; + +struct DataV1 { + int32_t v1; +}; + +struct DataV2 { + int32_t v1; + int32_t v2; +}; + +struct DataV3 { + int32_t v1; + int32_t v2; + int32_t v3; +}; + + +TEST(SerializeGrowable, WriteSessionsDataAtBufferEndAfterFlush) { + SerializationContext ctx; + ctx.createSerializer().growable(int8_t{}, [] (auto& s, auto& v) { }); + EXPECT_THAT(ctx.getBufferSize(), Eq(0)); + ctx.bw->flush(); + EXPECT_THAT(ctx.getBufferSize(), Gt(0)); +} + + +TEST(SerializeGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSessionsDataOffset) { + SerializationContext ctx; + + + constexpr size_t DATA_SIZE = 4; + int32_t data{}; + + ctx.createSerializer().growable(data, [](auto&s, auto& v) { s.value4b(v);}); + ctx.createDeserializer();//to flush data and create buffer reader + + EXPECT_THAT(ctx.getBufferSize(), Eq(3 + 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); + EXPECT_THAT(sessionEnd, Eq(DATA_SIZE)); + //this is the the offset from the end of buffer where actual data ends + uint16_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd + br.readBytes<2>(sessionsOffset); + EXPECT_THAT(sessionsOffset, Eq(1+2));//1byte for session info, 2 bytes for session offset variable + auto range = ctx.bw->getWrittenRange(); + auto dSize = std::distance(range.begin(), std::next(range.end(), -sessionsOffset)); + EXPECT_THAT(dSize, Eq(DATA_SIZE)); +} + +TEST(SerializeGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) { + SerializationContext 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]); + bitsery::details::readSize(*(ctx.br),sessionEnd[1]); + bitsery::details::readSize(*(ctx.br), sessionEnd[2]); + EXPECT_THAT(sessionEnd[0], Eq(12)); + EXPECT_THAT(sessionEnd[1], Eq(8)); + EXPECT_THAT(sessionEnd[2], Eq(12)); +} + +TEST(SerializeGrowable, WhenSessionsDataIsMoreThan0x7FFFThenWrite4BytesForSessionsOffset) { + SerializationContext ctx; + ctx.createSerializer(); + //create more sessions that can fit in 2 bytes + for (auto i = 0u; i < 0x8000; ++i) { + ctx.bw->beginSession(); + ctx.bw->endSession(); + } + ctx.createDeserializer();//to flush data and create buffer reader + EXPECT_THAT(ctx.getBufferSize(), Eq(0x8000+4)); + uint8_t tmp{}; + for (auto i = 0u; i < 0x8000; ++i) { + ctx.br->beginSession(); + ctx.br->endSession(); + } + + EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::NO_ERROR)); + ctx.br->readBytes<1>(tmp); + EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW)); +} + +TEST(SerializeGrowable, MultipleSessionsReadSameVersionData) { + SerializationContext ctx; + DataV2 data{8454,987451}; + ctx.createSerializer(); + auto& bw = (*ctx.bw); + 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 < 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(ctx.br->isCompletedSuccessfully(), Eq(true)); +} + +TEST(SerializeGrowable, MultipleSessionsReadNewerVersionData) { + SerializationContext ctx; + DataV3 data{8454,987451,54}; + ctx.createSerializer(); + auto& bw = (*ctx.bw); + 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(); + } + //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 < 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(br.isCompletedSuccessfully(), Eq(true)); +} + +TEST(SerializeGrowable, MultipleSessionsReadOlderVersionData) { + SerializationContext ctx; + DataV2 data{8454,987451}; + ctx.createSerializer(); + auto& bw = (*ctx.bw); + 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 + DataV3 res{4798,657891,985}; + auto& br = (*ctx.br); + 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(); + EXPECT_THAT(res.v1, Eq(data.v1)); + EXPECT_THAT(res.v2, Eq(data.v2)); + EXPECT_THAT(res.v3, Eq(0)); + } + EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true)); +} + +TEST(SerializeGrowable, MultipleNestedSessionsReadSameVersionData) { + 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(); + } + //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); + EXPECT_THAT(res.v1, Eq(data.v1)); + EXPECT_THAT(res.v2, Eq(data.v2)); + br.endSession(); + br.endSession(); + } + EXPECT_THAT(ctx.br->isCompletedSuccessfully(), Eq(true)); +} + +TEST(SerializeGrowable, MultipleNestedSessionsReadOlderVersionData) { + 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); + } + + //create more sessions that can fit in 2 bytes + ctx.createDeserializer();//to flush data and create buffer reader + 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(SerializeGrowable, MultipleNestedSessionsReadNewerVersionData1) { + SerializationContext 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); + 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(SerializeGrowable, MultipleNestedSessionsReadNewerVersionData2) { + SerializationContext 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); + 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)); +} + diff --git a/tests/serialization_objects.cpp b/tests/serialization_objects.cpp index 042b212..e44b660 100644 --- a/tests/serialization_objects.cpp +++ b/tests/serialization_objects.cpp @@ -24,13 +24,6 @@ #include #include "serialization_test_utils.h" -#include -#include - -#include - - - using testing::Eq; using testing::StrEq; using testing::ContainerEq; @@ -73,8 +66,8 @@ SERIALIZE(Y) auto writeInt = [](auto& s, auto& v) { s.template value(v); }; s.template text<1>(o.s, 10000); s.template value(o.y); - s.array(o.arr, writeInt); - s.array(o.carr, writeInt); + s.container(o.arr, writeInt); + s.container(o.carr, writeInt); s.container(o.vx, 10000, [](auto& s, auto& v) { s.object(v); }); } @@ -126,49 +119,4 @@ TEST(SerializeObject, GeneralConceptTest) { EXPECT_THAT(zres.x.s, StrEq(z.x.s)); EXPECT_THAT(zres.x.x, Eq(z.x.x)); -} - -TEST(DeltaSerializer, GeneralConceptTest) { - //std::string buf; - Y y{}; - y.y = 3423; - y.arr[0] = 111; - y.arr[1] = 222; - y.arr[2] = 333; - y.carr[0] = 123; - y.carr[1] = 456; - y.carr[2] = 789; - y.vx.push_back(X(234)); - y.vx.push_back(X(6245)); - y.vx.push_back(X(613461)); - y.s = "labal diena"; - y.vx[0].s = "very nice"; - y.vx[1].s = "very nice string, that is a little bit longer that previous"; - - Y yRead = y; - Y yNew = y; - yNew.y = 111111; - yNew.arr[2] = 0xFFFFFFFF; - yNew.carr[1] = 0xFFFFFFFF; - yNew.s = "labas dienaABC"; - yNew.vx[0].s = "very opapa"; - yNew.vx[1].s = "bla"; - yNew.vx.push_back(X{ 3 }); - - bitsery::DefaultConfig::BufferType buf; - bitsery::BufferWriter bw{ buf }; - bitsery::DeltaSerializer ser(bw, y, yNew); - serialize(ser, yNew); - bw.flush(); - - bitsery::BufferReader br{ bw.getWrittenRange() }; - bitsery::DeltaDeserializer des(br, y, yRead); - serialize(des, yRead); - - EXPECT_THAT(yRead.y, Eq(yNew.y)); - EXPECT_THAT(yRead.vx, ContainerEq(yNew.vx)); - EXPECT_THAT(yRead.arr, ContainerEq(yNew.arr)); - EXPECT_THAT(yRead.carr, ContainerEq(yNew.carr)); - EXPECT_THAT(yRead.s, StrEq(yNew.s)); -} - +} \ No newline at end of file diff --git a/tests/serialization_range.cpp b/tests/serialization_range.cpp index 4db263d..8f7fe2b 100644 --- a/tests/serialization_range.cpp +++ b/tests/serialization_range.cpp @@ -152,3 +152,15 @@ TEST(SerializeRange, DoubleUsingBitsSizeConstraint2) { EXPECT_THAT(ctx.getBufferSize(), Eq(7)); EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) / (static_cast>(1) << bits))); } + +TEST(SerializeRange, WhenDataIsInvalidThenReturnMinimumRangeValue) { + SerializationContext ctx; + constexpr RangeSpec r1{4, 10};//6 is max, but 3bits required + int res1; + uint8_t tmp{0xFF};//write all 1 so when reading 3 bits we get 7 + ctx.createSerializer().value1b(tmp); + ctx.createDeserializer().range(res1, r1); + + EXPECT_THAT(ctx.getBufferSize(), Eq(1)); + EXPECT_THAT(res1, Eq(4)); +} diff --git a/tests/serialization_test_utils.h b/tests/serialization_test_utils.h index 073225e..78e8528 100644 --- a/tests/serialization_test_utils.h +++ b/tests/serialization_test_utils.h @@ -21,8 +21,8 @@ //SOFTWARE. -#ifndef BITSERY_SERIALIZERTESTS_H -#define BITSERY_SERIALIZERTESTS_H +#ifndef BITSERY_SERIALIZER_TEST_UTILS_H +#define BITSERY_SERIALIZER_TEST_UTILS_H #include #include @@ -82,10 +82,11 @@ SERIALIZE(MyStruct2) { class SerializationContext { +public: bitsery::DefaultConfig::BufferType buf{}; std::unique_ptr bw; std::unique_ptr br; -public: + bitsery::Serializer createSerializer() { bw = std::make_unique(buf); return {*bw}; @@ -113,4 +114,4 @@ public: }; }; -#endif //BITSERY_SERIALIZERTESTS_H +#endif //BITSERY_SERIALIZER_TEST_UTILS_H diff --git a/tests/serialization_values.cpp b/tests/serialization_values.cpp index 0f60242..ea1768c 100644 --- a/tests/serialization_values.cpp +++ b/tests/serialization_values.cpp @@ -65,8 +65,8 @@ TEST(SerializeValues, ValueSizeOverload1Byte) { constexpr size_t TSIZE = sizeof(v); SerializationContext ctx; - ctx.createSerializer().value1(v); - ctx.createDeserializer().value1(res); + ctx.createSerializer().value1b(v); + ctx.createDeserializer().value1b(res); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(TSIZE, Eq(ctx.getBufferSize())); @@ -78,8 +78,8 @@ TEST(SerializeValues, ValueSizeOverload2Byte) { constexpr size_t TSIZE = sizeof(v); SerializationContext ctx; - ctx.createSerializer().value2(v); - ctx.createDeserializer().value2(res); + ctx.createSerializer().value2b(v); + ctx.createDeserializer().value2b(res); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(TSIZE, Eq(ctx.getBufferSize())); @@ -91,8 +91,8 @@ TEST(SerializeValues, ValueSizeOverload4Byte) { constexpr size_t TSIZE = sizeof(v); SerializationContext ctx; - ctx.createSerializer().value4(v); - ctx.createDeserializer().value4(res); + ctx.createSerializer().value4b(v); + ctx.createDeserializer().value4b(res); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(TSIZE, Eq(ctx.getBufferSize())); @@ -104,8 +104,8 @@ TEST(SerializeValues, ValueSizeOverload8Byte) { constexpr size_t TSIZE = sizeof(v); SerializationContext ctx; - ctx.createSerializer().value8(v); - ctx.createDeserializer().value8(res); + ctx.createSerializer().value8b(v); + ctx.createDeserializer().value8b(res); EXPECT_THAT(res, Eq(v)); EXPECT_THAT(TSIZE, Eq(ctx.getBufferSize()));