mirror of
https://github.com/fraillt/bitsery.git
synced 2026-08-27 13:38:34 +00:00
new stream adapter, and lots of refactorings
This commit is contained in:
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,27 +1,33 @@
|
||||
# [4.0.0](https://github.com/fraillt/bitsery/compare/v3.0.0...v4.0.0) (2017-10-02)
|
||||
|
||||
new flexible syntax
|
||||
traits changed,
|
||||
container get isContiguous
|
||||
text is separate from container, only has length, and addNUL
|
||||
buffer traits removed difference type
|
||||
improved reading, writing performance (because of isContiguous and difference_type)
|
||||
BasicBufferWriter/Reader no longer has bit-packing operations by default
|
||||
Bit-packing is enabled via template parameter in Serializer/Deserializer, additionally added new method enableBitPacking.
|
||||
Additionally Serializer/Deserializer is no longer copyable, because it stores bit-packer state.
|
||||
ExtensionTraits gain additional patameter BitPackingRequired, static_asserts if bit-packing is not enabled.
|
||||
Removed boolByte, boolBit, and added boolValue and it writes bit or byte, depeding on if bit-packing is enabled or not.
|
||||
added missing std containers support: forward_list, deque, stack, queue, priority_queue, set, multiset, unordered_set, unordered_multiset
|
||||
Renamed ContainerMap to StdMap, Optional to StdOptional
|
||||
Improved error messages
|
||||
Lots of renaming ...
|
||||
Added adapters for easier extension
|
||||
Config no longer needs typedef *Buffer*
|
||||
I feel that current library public API is complete, and should be stable for long time.
|
||||
Most changes was made to improve performance or/and make library usage easier.
|
||||
|
||||
### Features
|
||||
|
||||
todo write tests:
|
||||
bufferreader accepts const data
|
||||
* new **flexible** syntax similar to *cereal* library.
|
||||
This syntax no longer requires to specify explicit fundamental type sizes and container maxsize (container max size can be enforced by special function *maxSize*).
|
||||
Be careful when using deserializing untrusted data and make sure to enforce fundamental type sizes when using on multiple platforms.
|
||||
(use helper function *assertFundamentalTypeSizes* to enforce type sizes for multiple platforms)
|
||||
* added streaming support, by introducing new **adapter** concept. Two adapter implementations is available: stream adapter, or buffer adapter.
|
||||
* added missing std containers support: forward_list, deque, stack, queue, priority_queue, set, multiset, unordered_set, unordered_multiset.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* a lot of classes and files were renamed.
|
||||
* improved error messages.
|
||||
* traits reworked:
|
||||
* ContainerTraits get **isContiguous**.
|
||||
* TextTraits is separate from ContainerTraits, only has **length**, and **addNUL**.
|
||||
* buffer traits renamed to **BufferAdapterTraits** and removed difference type.
|
||||
* added **TValue** to all trait types, this is used to diagnose better errors.
|
||||
* BasicBufferReader/Writer is split in two different types: **AdapterReader/Writer** and separate type that enables bit-packing operations **AdapterBitPacking(Reader/Writer)Wrapper**.
|
||||
* Serializer/Deserializer reworked
|
||||
* No longer copyable, because it stores adapter writer/reader.
|
||||
* Removed boolByte, boolBit, and added **boolValue** and it writes bit or byte, depeding on if bit-packing is enabled or not.
|
||||
* Bit-packing is enabled by calling **enableBitPacking**, if bitpacking is already enabled, this method will return same instance.
|
||||
* changed defaults for *DefaultConfig*, BufferSessionsEnabled is false by default, because it doesn't work with input streams.
|
||||
* serialization config no longer needs typedef *Buffer*.
|
||||
|
||||
# [3.0.0](https://github.com/fraillt/bitsery/compare/v2.0.1...v3.0.0) (2017-09-21)
|
||||
|
||||
@@ -76,7 +82,7 @@ bufferreader accepts const data
|
||||
|
||||
### Features
|
||||
|
||||
* Endianness support, default network configuration is *little endian*
|
||||
* endianness support, default network configuration is *little endian*
|
||||
* added user extensible function **ext**, to work with objects that require different serialization/deserialization path (e.g. pointers)
|
||||
* **optional** extension (for *ext* function), to work with *std::optional* types
|
||||
|
||||
|
||||
44
README.md
44
README.md
@@ -14,12 +14,14 @@ All cross-platform requirements are enforced at compile time, so serialized data
|
||||
|
||||
* Cross-platform compatible.
|
||||
* Optimized for speed and space.
|
||||
* Allows flexible or/and verbose syntax for better serialization control.
|
||||
* No code generation required: no IDL or metadata, just use your types directly.
|
||||
* Runtime error checking on deserialization.
|
||||
* Supports forward/backward compatibility for your types.
|
||||
* 2-in-1 declarative control flow, same code for serialization and deserialization.
|
||||
* Allows fine-grained bit-level serialization control.
|
||||
* Easily extendable.
|
||||
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
|
||||
* Easily extendable for any type.
|
||||
* Configurable endianess support.
|
||||
* No macros.
|
||||
|
||||
@@ -41,38 +43,44 @@ If still not convinced read more in library [motivation](doc/design/README.md) s
|
||||
## Usage example
|
||||
```cpp
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
char str[6];
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.text1b(o.str);
|
||||
s.container4b(o.fs, 100);
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
std::vector<uint8_t> buffer;
|
||||
BufferWriter bw{buffer};
|
||||
Serializer ser{bw};
|
||||
|
||||
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
|
||||
ser.object(data); // serializes data
|
||||
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Deserializer des{br};
|
||||
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
des.object(res); //deserializes data
|
||||
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
```
|
||||
For more details go directly to [Quick start](doc/tutorial/hello_world.md) tutorial.
|
||||
For more details go directly to [quick start](doc/tutorial/hello_world.md) tutorial.
|
||||
|
||||
## How to use it
|
||||
This documentation comprises these parts:
|
||||
|
||||
@@ -2,51 +2,65 @@ 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:
|
||||
* `valueNb instead of value`
|
||||
* `fundamental types`
|
||||
* `valueNb instead of value`
|
||||
* `flexible syntax`
|
||||
* `serializer/deserializer functions overloads`
|
||||
* `extending library functionality`
|
||||
* `errors handling`
|
||||
* `forward/backward compatibility via Growable extension`
|
||||
|
||||
|
||||
Core Serializer/Deserializer functions (alphabetical order):
|
||||
* `align`
|
||||
* `boolByte`
|
||||
* `boolBit`
|
||||
* `boolValue`
|
||||
* `container`
|
||||
* `extend`
|
||||
* `getContext`
|
||||
* `ext`
|
||||
* `context`
|
||||
* `object`
|
||||
* `text`
|
||||
* `value`
|
||||
|
||||
Serializer/Deserializer extensions via `extend` method (alphabetical order):
|
||||
* `ContainerMap`
|
||||
Serializer/Deserializer extensions via `ext` method (alphabetical order):
|
||||
* `Entropy`
|
||||
* `Growable`
|
||||
* `Optional`
|
||||
* `StdMap`
|
||||
* `StdOptional`
|
||||
* `StdQueue`
|
||||
* `StdSet`
|
||||
* `StdStack`
|
||||
* `ValueRange`
|
||||
|
||||
BasicBufferWriter/Reader functions:
|
||||
AdapterWriter/Reader functions:
|
||||
* `writeBits/readBits`
|
||||
* `writeBytes/readBytes`
|
||||
* `writeBuffer/readBuffer`
|
||||
* `align`
|
||||
* `beginSession/endSession`
|
||||
* `flush (writer only)`
|
||||
* `writtenBytesCount (writer only)`
|
||||
* `setError (reader only)`
|
||||
* `getError (reader only)`
|
||||
* `isCompletedSuccessfully (reader only)`
|
||||
|
||||
Input adapters (buffer and stream) functions:
|
||||
* `read`
|
||||
* `error`
|
||||
* `setError`
|
||||
* `isCompletedSuccessfully`
|
||||
|
||||
Output adapters (buffer and stream) functions:
|
||||
* `write`
|
||||
* `flush`
|
||||
* `writtenBytesCount`
|
||||
|
||||
|
||||
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.
|
||||
* 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.
|
||||
|
||||
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** extension, serialized buffer size in bytes, cannot be greater than 2^(n-2) (where n = sizeof(std::size_t) * 8).
|
||||
|
||||
Other:
|
||||
* [Contributing](../CONTRIBUTING.md)
|
||||
* [Change log](../CHANGELOG.md)
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Most well-known serialization libraries sacrifice memory and speed efficiency by
|
||||
## A word about JSON
|
||||
|
||||
Often times 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).
|
||||
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.
|
||||
|
||||
Adding optional support for JSON doesn't come for free either.
|
||||
@@ -34,18 +34,19 @@ Now let's review features in more detail.
|
||||
* **Cross-platform compatible.** if same code compiles on Android, PS3 console, and your PC either x64 or x86 architecture, you are 100% sure it works.
|
||||
To achieve this, bitsery specifically defines size of underlying data, hence syntax is *value\<2\>* (alias function *value2b*) instead or *value*, or *container2b* for element type of 16bits, eg int16_t.
|
||||
Bitsery also applies endianess transformation if nessesarry.
|
||||
**If** however, you don't like this verbose syntax, you can just write *serialize* functions for fundamental types, and forget about *value\<N\>*, *container\<N\>*, etc.
|
||||
But do it on your own risk, or write static asserts.
|
||||
* **Flexible syntax.** if you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b* you can use flexible syntax.
|
||||
Just include <bitsery/flexible.h> and can write like in [cereal](http://uscilab.github.io/cereal/).
|
||||
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it accross multiple platforms.
|
||||
* **Optimized for speed and space.** library itself doesn't do any allocations (except if you use backward/forward compatibility) so data writing/reading is fast as memcpy to/from your buffer.
|
||||
It also doesn't serialize any type information, all information needed is writen in your code!
|
||||
* **No code generation required: no IDL or metadata** since it doesn't support any other formats except binary, it doesn't need any metadata.
|
||||
* **Runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*, this way bitsery ensures that no malicious data will not crash you.
|
||||
* **Runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*, this way bitsery ensures that no malicious data crash you.
|
||||
* **Supports forward/backward compatibility for your types** library has optional forward/backward compatibility for types implemented in *BasicBufferReader/BasicBufferWriter* by allowing to have inner data sessions in inside buffer.
|
||||
This is the only functionality that requires dynamic memory allocation.
|
||||
*Glowable* extension use these sessions to add compatibility support for your types, in most basic form.
|
||||
You can implement your own extensions if you want to be able to add default values.
|
||||
* **2-in-1 declarative control flow, same code for serialization and deserialization.** only one function to define, for serialization and deserialization in same manner as *cereal* does.
|
||||
It might be handy to have separate *load*, *save* functions, but Bitsery explicitly doesn't support it, to avoid any serialization deserialization path differences, because it is very hard to catch an errors if you make a bug in one of these functions.
|
||||
It might be handy to have separate *load* and *save* functions, but Bitsery explicitly doesn't support it, to avoid any serialization deserialization divergence, because it is very hard to catch an errors if you make a bug in one of these functions.
|
||||
The only way around this through extensions, write your custom flow once, and reuse where you need them.
|
||||
* **Allows fine-grained serialization control** this is a feature that no other libraries provides.
|
||||
Bitsery allows to use bit-level operations and has two extensions that use them:
|
||||
@@ -53,16 +54,16 @@ Bitsery allows to use bit-level operations and has two extensions that use them:
|
||||
* *Entropy*,- full term is *entropy encoding*, which means that when you have most common value, or multiple values, it will write just few bits instead of full object.
|
||||
|
||||
Eg.: imagine that you have a struct Person{ int32_t Id; string Profession; }.
|
||||
You know that mostly there are young persons, so the most common value will be equal to: "Student", "Child", "NoProfession", in this case you'll pay 2bits for each record, but write no data if string matches.
|
||||
You might know that mostly there are young persons, so the most common value will be equal to: "Student", "Child", "NoProfession", in this case you'll pay 2bits for each record, but write no data if string matches.
|
||||
|
||||
Using these bit-level operations and extensions you can compose your own extensions for vectors, matrices or any other types.
|
||||
Further more, all other operations will not align data automatically for you, so data will be compressed as much as possible.
|
||||
|
||||
One more advanced and dangerous feature, is ability to have serialization context, so you can control your serialization flow at runtime, but make sure that these contexts are in sync between serializer and deserializer.
|
||||
One possible use case for serialization context is to pass min/max ranges for *ValueRange* when your information changes at runtime.
|
||||
* One more advanced and dangerous feature, is ability to have serialization context, so you can control your serialization flow at runtime, but make sure that these contexts are in sync between serializer and deserializer.
|
||||
One possible use case for serialization context is to pass min/max ranges for *ValueRange* when your information changes at runtime.
|
||||
* **Easily extendable** library is designed to be easily extendable for any type and flow.
|
||||
You want to support your custom container, its fine there is *ContainerTraits* for this, only few methods required to implement.
|
||||
To use same container for buffer writing/reading add specialization to *BufferContainerTraits*.
|
||||
To use same container for buffer writing/reading add specialization to *BufferAdapterTraits*.
|
||||
You want to customize serialization flow - use extensions, only two methods to define, and *ExtensionTraits* to further customize usage.
|
||||
* **Configurable endianess support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
|
||||
* **No macros.** Not so much to say, if you are like me, then it's a feature :)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
*document in progress*
|
||||
* NO_ERROR,
|
||||
* NoError,
|
||||
* BUFFER_OVERFLOW,
|
||||
* INVALID_BUFFER_DATA
|
||||
* write what happens when data is corrupted
|
||||
|
||||
@@ -10,7 +10,30 @@ bitsery can be directly included in your project or installed anywhere you can a
|
||||
Grab the latest version, and include directory `bitsery_base_dir/include/` to your project.
|
||||
There's nothing to build or make - **bitsery** is header only.
|
||||
|
||||
## Add serialization methods for your types
|
||||
## Include required headers and define some helper types
|
||||
|
||||
```cpp
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
```
|
||||
|
||||
**bitsery** is very lightweight, so we need to explicitly include what we need.
|
||||
* `<bitsery/bitsery.h>` is a core header, that includes our Serializer and Deserializer
|
||||
* `<bitsery/adapter/buffer.h>` in order to write/read data we need specific adapter, depending on what underlying buffer will be. In this example we'll be using std::vector as our buffer, so we include buffer adapter.
|
||||
* <bitsery/traits/...> traits tells library how efficiently serialize particular container.
|
||||
|
||||
create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
|
||||
|
||||
## Add serialization method for your type
|
||||
|
||||
**bitsery** needs to know which data members to serialize in your classes.
|
||||
Let it know by implementing a serialize method for your type:
|
||||
@@ -30,11 +53,11 @@ void serialize(S& s, MyStruct& o) {
|
||||
};
|
||||
```
|
||||
|
||||
**bitsery** also can serialize private class members, just move *serialize* function inside structure, and make it *friend* (*fiend void serialize(.....)*).
|
||||
**bitsery** also allows to define serialize function in side your class, and can also serialize private class members, just make *friend bitsery::Access;*
|
||||
|
||||
**bitsery** has verbose syntax, because it is cross-platform compatible by default and has full control over how to serialize data (read more about it in [motivation](../design/README.md))
|
||||
**bitsery** supports two ways how to describe your serialization flow: *verbose syntax* (as in example) or *flexible syntax*, similar to *cereal* library, just include `<bitsery/flexible.h>` to use it.
|
||||
|
||||
This example contains core functionality that you'll use all the time, so lets get through it:
|
||||
This example we choosed probably unfamiliar verbose syntax, so lets explain core functionality that you'll use all the time:
|
||||
* **s.value4b(o.i);** serialize fundamental types (ints, floats, enums) value**4b** means, that data type is 4 bytes. If you use same code on different machines, if it compiles it means it is compatible.
|
||||
* **s.text1b(o.str);** serialize text (null-terminated) of char type, if you use *wchar* then you would write *text2b*.
|
||||
* **s.container4b(o.fs, 100);** serializes any container of fundamental types of size 4bytes, **100** is max size of container.
|
||||
@@ -45,57 +68,35 @@ External serialization functions should be placed either in the same namespace a
|
||||
|
||||
## Serialization and deserialization
|
||||
|
||||
### Create serializer
|
||||
Create a serializer and send the data you want to serialize to it.
|
||||
Create buffer and use helper functions for serialization and deserialization.
|
||||
|
||||
```cpp
|
||||
std::vector<uint8_t> buffer;
|
||||
BufferWriter bw{buffer};
|
||||
Serializer ser{bw};
|
||||
Buffer buffer;
|
||||
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
```
|
||||
|
||||
Serialization process consists of three independant parts.
|
||||
* **std::vector<uint8_t> buffer;** core object, that will store the data for serialization and deserialization.
|
||||
* **BufferWriter bw{buffer};** writer knows how to write bytes to buffer, and how to resize buffer, or how to use fixed-size buffer. It also applies endianess transformations if nesessary.
|
||||
* **Serializer ser{bw};** serializer is a high level wrapper that knows how to convert object to stream of bytes, and write then to buffer.
|
||||
|
||||
Serializer doesn't store any state, it only has reference to buffer, so it is safe to create many of those if nesessary.
|
||||
|
||||
BufferWriter also doesn't own buffer, but it stores state about writing position and container size.
|
||||
|
||||
One important note that when using bit-level operations, dont forget to flush buffer writer **bw.flush()** otherwise, some data might not be written to buffer.
|
||||
|
||||
|
||||
### Serialize object
|
||||
|
||||
```cpp
|
||||
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
|
||||
ser.object(data); // serializes data
|
||||
```
|
||||
|
||||
**ser.object(data)** is a final core function along with **value, text, container**.
|
||||
|
||||
This function is actually equivalent to calling *serialize(ser, data)* directly, but it displays friendly static assert message if it cannot find *serialize* function for your type.
|
||||
|
||||
### Deserialize object
|
||||
|
||||
```cpp
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Deserializer des{br};
|
||||
|
||||
MyStruct res{};
|
||||
des.object(res); //deserializes data
|
||||
```
|
||||
|
||||
Deserialization process is equivalent to serialization, except that *BufferReader* reader has getError() method that returns deserialization state.
|
||||
These helper functions use default configuration *bitsery::DefaultConfig*
|
||||
* **quickSerialization** create serializer using output adapter, serializes data and returns written size.
|
||||
* **quickDeserialization** create deserializer using input adapter, deserializes to object, and returns deserialization state.
|
||||
deserialization state has two properties, error code and bool that indicates if buffer was fully read and there is no errors.
|
||||
|
||||
## Full example code
|
||||
|
||||
```cpp
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
char str[6];
|
||||
@@ -110,18 +111,16 @@ void serialize(S& s, MyStruct& o) {
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::vector<uint8_t> buffer;
|
||||
BufferWriter bw{buffer};
|
||||
Serializer ser{bw};
|
||||
|
||||
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
|
||||
ser.object(data); // serializes data
|
||||
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Deserializer des{br};
|
||||
|
||||
MyStruct res{};
|
||||
des.object(res); //deserializes data
|
||||
|
||||
Buffer buffer;
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//include bitsery.h to get serialization and deserialization classes
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
//in ordered to serialize/deserialize data to buffer, include buffer adapter
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//bitsery itself doesn't is lightweight, and doesnt include any unnessessary files,
|
||||
//traits helps library to know how to use types correctly,
|
||||
//in this case we'll be using vector both, to serialize/deserialize data and to store use as a buffer.
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
@@ -12,13 +17,14 @@ struct MyStruct {
|
||||
//define how object should be serialized/deserialized
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value4b(o.i);//fundamental types (ints, floats, enums) of size 4b
|
||||
s.value2b(o.e);
|
||||
s.container4b(o.fs, 10);
|
||||
s.container4b(o.fs, 10);//resizable containers also requires maxSize, to make it safe from buffer-overflow attacks
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
@@ -29,12 +35,16 @@ int main() {
|
||||
MyStruct res{};
|
||||
|
||||
//create buffer to store data
|
||||
std::vector<uint8_t> buffer;
|
||||
Buffer buffer;
|
||||
//use quick serialization function,
|
||||
//it will use default configuration to setup all the nesessary steps
|
||||
//and serialize data to container
|
||||
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
|
||||
|
||||
auto writtenSize = startSerialization<OutputAdapter>(buffer, data);
|
||||
//same as serialization, but returns deserialization state as a pair
|
||||
//first = error code, second = is buffer was successfully read from begin to the end.
|
||||
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
|
||||
|
||||
auto state = startDeserialization<InputAdapter>(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
|
||||
assert(state.first == ReaderError::NO_ERROR && state.second);
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
#include <bitsery/ext/growable.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//include traits for types, that we'll be using
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
//include extension that will allow to have backward/forward compatibility
|
||||
#include <bitsery/ext/growable.h>
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
@@ -15,6 +17,17 @@ namespace MyTypes {
|
||||
struct Weapon {
|
||||
std::string name;
|
||||
int16_t damage;
|
||||
private:
|
||||
//define serialize function as private, and give access to bitsery
|
||||
friend bitsery::Access;
|
||||
template <typename S>
|
||||
void serialize (S& s) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(*this, bitsery::ext::Growable{}, [&s](Weapon& o1) {
|
||||
s.text1b(o1.name, 20);
|
||||
s.value2b(o1.damage);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct Monster {
|
||||
@@ -36,16 +49,6 @@ namespace MyTypes {
|
||||
s.value4b(o.z);
|
||||
}
|
||||
|
||||
//define serialization functions
|
||||
template <typename S>
|
||||
void serialize (S& s, Weapon& o) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(o, bitsery::ext::Growable{}, [&s](Weapon& o1) {
|
||||
s.text1b(o1.name, 20);
|
||||
s.value2b(o1.damage);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void serialize (S& s, Monster& o) {
|
||||
//forward/backward compatibility for monsters
|
||||
@@ -65,10 +68,12 @@ namespace MyTypes {
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
using Buffer = std::array<uint8_t, 1000000>;
|
||||
//use fixed-size buffer
|
||||
using Buffer = std::array<uint8_t, 10000>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
//create configuration that enables session support, to work with "growable" extension
|
||||
struct SessionsEnabled:public DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
@@ -77,13 +82,22 @@ int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
data.weapons.push_back(MyTypes::Weapon{"GoodWeapon", 100});
|
||||
|
||||
//1) create buffer to store data
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
auto writtenSize = startSerialization<OutputAdapter, MyTypes::Monster, SessionsEnabled>(buffer, data);
|
||||
//since we're using different configuration, we cannot use quickSerialization function.
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, SessionsEnabled>> ser{OutputAdapter{buffer}};
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
|
||||
|
||||
//deserialize same object, can also be invoked like this: serialize(des, data)
|
||||
MyTypes::Monster res{};
|
||||
auto state = startDeserialization<InputAdapter, MyTypes::Monster, SessionsEnabled>(InputAdapter{buffer.begin(), writtenSize}, res);
|
||||
assert(state.first == ReaderError::NO_ERROR && state.second);
|
||||
//deserialize
|
||||
BasicDeserializer<AdapterReader<InputAdapter, SessionsEnabled>> des{InputAdapter{buffer.begin(), writtenSize}};
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
}
|
||||
@@ -24,22 +24,39 @@
|
||||
#ifndef BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
#define BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
|
||||
#include "../details/buffer_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//base class that stores container iterators, and is required for session support (for reading sessions data)
|
||||
template <typename Buffer>
|
||||
class InputBufferAdapter {
|
||||
class BufferIterators {
|
||||
protected:
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
|
||||
BufferIterators(TIterator begin, TIterator end)
|
||||
:posIt{begin},
|
||||
endIt{end}
|
||||
{
|
||||
}
|
||||
|
||||
friend details::SessionAccess;
|
||||
|
||||
TIterator posIt;
|
||||
TIterator endIt;
|
||||
};
|
||||
|
||||
|
||||
template <typename Buffer>
|
||||
class InputBufferAdapter: public BufferIterators<Buffer> {
|
||||
public:
|
||||
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
|
||||
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator end)
|
||||
:_pos{begin},
|
||||
_end{end}
|
||||
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,43 +67,38 @@ namespace bitsery {
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = _pos;
|
||||
_pos += size;
|
||||
if (std::distance(_pos, _end) >= 0) {
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, this->endIt) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
_pos -= size;
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (getError() == ReaderError::NO_ERROR)
|
||||
setError(ReaderError::DATA_OVERFLOW);
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderError getError() const {
|
||||
auto res = std::distance(_end, _pos);
|
||||
ReaderError error() const {
|
||||
auto res = std::distance(this->endIt, this->posIt);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<ReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return ReaderError::NO_ERROR;
|
||||
return ReaderError::NoError;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
_end = _pos;
|
||||
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
|
||||
std::advance(_pos, static_cast<size_t>(error));
|
||||
this->endIt = this->posIt;
|
||||
//to avoid creating temporary for error state, mark an error by passing posIt after the endIt
|
||||
std::advance(this->posIt, static_cast<size_t>(error));
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _pos == _end;
|
||||
return this->posIt == this->endIt;
|
||||
}
|
||||
private:
|
||||
friend details::SessionAccess;
|
||||
|
||||
TIterator _pos;
|
||||
TIterator _end;
|
||||
};
|
||||
|
||||
|
||||
@@ -111,7 +123,11 @@ namespace bitsery {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
|
||||
size_t getWrittenBytesCount() const {
|
||||
void flush() {
|
||||
//this function might be useful for stream adapters
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return static_cast<size_t>(std::distance(std::begin(_buffer), _outIt));
|
||||
}
|
||||
|
||||
@@ -135,7 +151,7 @@ namespace bitsery {
|
||||
_outIt = std::begin(_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::true_type) {
|
||||
void writeInternal(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
121
include/bitsery/adapter/stream.h
Normal file
121
include/bitsery/adapter/stream.h
Normal file
@@ -0,0 +1,121 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_ADAPTERS_DYNAMIC_STREAM_H
|
||||
#define BITSERY_ADAPTERS_DYNAMIC_STREAM_H
|
||||
|
||||
#include "../details/adapter_common.h"
|
||||
#include "../traits/core/traits.h"
|
||||
#include <ios>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicInputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
|
||||
:_ios{istream} {}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
_ios.rdbuf()->sgetn( data , size );
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
if (!_ios.bad())
|
||||
return ReaderError::NoError;
|
||||
return _ios.eof()
|
||||
? ReaderError::DataOverflow
|
||||
: ReaderError::ReadingError;
|
||||
}
|
||||
bool isCompletedSuccessfully() const {
|
||||
if (error() == ReaderError::NoError) {
|
||||
return _ios.rdbuf()->sgetc() == CharTraits::eof();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void setError(ReaderError error) {
|
||||
//has no effect when using
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>& _ios;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicOutputStreamAdapter {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream):_ios{ostream} {}
|
||||
|
||||
void write(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
_ios.rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(&_ios))
|
||||
ostream->flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
//streaming doesn't return written bytes
|
||||
return 0;
|
||||
}
|
||||
|
||||
//this method is only for stream writing
|
||||
bool isValidState() const {
|
||||
return !_ios.bad();
|
||||
}
|
||||
|
||||
private:
|
||||
std::basic_ios<TChar, CharTraits>& _ios;
|
||||
};
|
||||
|
||||
template <typename TChar, typename CharTraits>
|
||||
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, CharTraits>, public BasicOutputStreamAdapter<TChar, CharTraits> {
|
||||
public:
|
||||
using TValue = TChar;
|
||||
using TIterator = void;//TIterator is used with sessions, but streams cannot be used with sessions
|
||||
|
||||
//both bases contain reference to same iostream, so no need to do anything
|
||||
BasicIOStreamAdapter(std::basic_ios<TChar, CharTraits>& iostream)
|
||||
:BasicInputStreamAdapter<TChar, CharTraits>{iostream},
|
||||
BasicOutputStreamAdapter<TChar, CharTraits>{iostream} {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//helper types for most common implementations for std streams
|
||||
using OutputStreamAdapter = BasicOutputStreamAdapter<char, std::char_traits<char>>;
|
||||
using InputStreamAdapter = BasicInputStreamAdapter<char, std::char_traits<char>>;
|
||||
using IOStreamAdapter = BasicIOStreamAdapter<char, std::char_traits<char>>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTERS_DYNAMIC_STREAM_H
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_BUFFER_READER_H
|
||||
#define BITSERY_BUFFER_READER_H
|
||||
#ifndef BITSERY_BASIC_READER_H
|
||||
#define BITSERY_BASIC_READER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
#include <algorithm>
|
||||
@@ -33,33 +33,34 @@
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TReader>
|
||||
class BitPackingReader;
|
||||
class AdapterReaderBitPackingWrapper;
|
||||
|
||||
template<typename Config, typename InputAdapter>
|
||||
struct BasicReader {
|
||||
template<typename InputAdapter, typename Config>
|
||||
struct AdapterReader {
|
||||
//this is required by deserializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
using TValue = typename InputAdapter::TValue;
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit BasicReader(InputAdapter adapter)
|
||||
: _bufferContext{std::move(adapter)},
|
||||
_session{*this, _bufferContext}
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
|
||||
explicit AdapterReader(InputAdapter&& adapter)
|
||||
: _inputAdapter{std::move(adapter)},
|
||||
_session{*this, _inputAdapter}
|
||||
{
|
||||
}
|
||||
|
||||
BasicReader(const BasicReader &) = delete;
|
||||
AdapterReader(const AdapterReader &) = delete;
|
||||
|
||||
BasicReader &operator=(const BasicReader &) = delete;
|
||||
AdapterReader &operator=(const AdapterReader &) = delete;
|
||||
|
||||
BasicReader(BasicReader &&) noexcept = default;
|
||||
AdapterReader(AdapterReader &&) noexcept = default;
|
||||
|
||||
BasicReader &operator=(BasicReader &&) noexcept = default;
|
||||
AdapterReader &operator=(AdapterReader &&) noexcept = default;
|
||||
|
||||
~BasicReader() noexcept = default;
|
||||
~AdapterReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
@@ -86,45 +87,49 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _bufferContext.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
return _inputAdapter.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
}
|
||||
|
||||
ReaderError getError() const {
|
||||
auto err = _bufferContext.getError();
|
||||
if (_session.hasActiveSessions() && err == ReaderError::DATA_OVERFLOW)
|
||||
return ReaderError::NO_ERROR;
|
||||
ReaderError error() const {
|
||||
auto err = _inputAdapter.error();
|
||||
if (err == ReaderError::DataOverflow && _session.hasActiveSessions())
|
||||
return ReaderError::NoError;
|
||||
return err;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
return _bufferContext.setError(error);
|
||||
return _inputAdapter.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
if (getError() != ReaderError::INVALID_DATA) {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
if (getError() != ReaderError::INVALID_DATA) {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.end();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BitPackingReader<BasicReader<Config, InputAdapter>>;
|
||||
const InputAdapter& adapter() const {
|
||||
return _inputAdapter;
|
||||
}
|
||||
|
||||
InputAdapter _bufferContext;
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
InputAdapter _inputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsReader<BasicReader<Config, InputAdapter>>,
|
||||
session::DisabledSessionsReader<BasicReader<Config, InputAdapter>>>::type
|
||||
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_bufferContext.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
@@ -143,27 +148,25 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename TReader>
|
||||
struct BitPackingReader {
|
||||
struct AdapterReaderBitPackingWrapper {
|
||||
//this is required by deserializer
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
//make TValue unsigned for bitpacking
|
||||
using UnsignedValue = typename std::make_unsigned<typename TReader::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
using TValue = typename TReader::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
|
||||
explicit BitPackingReader(TReader& reader):_reader{reader}
|
||||
explicit AdapterReaderBitPackingWrapper(TReader& reader):_reader{reader}
|
||||
{
|
||||
static_assert(std::is_unsigned<TValue>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(TValue) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(TValue) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BitPackingReader(const BitPackingReader&) = delete;
|
||||
BitPackingReader& operator = (const BitPackingReader&) = delete;
|
||||
AdapterReaderBitPackingWrapper(const AdapterReaderBitPackingWrapper&) = delete;
|
||||
AdapterReaderBitPackingWrapper& operator = (const AdapterReaderBitPackingWrapper&) = delete;
|
||||
|
||||
BitPackingReader(BitPackingReader&& ) noexcept = default;
|
||||
BitPackingReader& operator = (BitPackingReader&& ) noexcept = default;
|
||||
AdapterReaderBitPackingWrapper(AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterReaderBitPackingWrapper& operator = (AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~BitPackingReader() {
|
||||
~AdapterReaderBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ namespace bitsery {
|
||||
if (!m_scratchBits)
|
||||
_reader.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BITS_SIZE<T>::value);
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
@@ -190,7 +193,7 @@ namespace bitsery {
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BITS_SIZE<T>::value);
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +209,7 @@ namespace bitsery {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(ReaderError::INVALID_DATA);
|
||||
setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +217,8 @@ namespace bitsery {
|
||||
return _reader.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError getError() const {
|
||||
return _reader.getError();
|
||||
ReaderError error() const {
|
||||
return _reader.error();
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
@@ -242,12 +245,12 @@ namespace bitsery {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BITS_SIZE<TValue>::value);
|
||||
auto bits = std::min(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
TValue tmp;
|
||||
_reader.template readBytes<sizeof(TValue), TValue>(tmp);
|
||||
UnsignedValue tmp;
|
||||
_reader.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BITS_SIZE<TValue>::value;
|
||||
m_scratchBits += details::BitsSize<UnsignedValue>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
@@ -22,10 +22,9 @@
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_BUFFER_WRITER_H
|
||||
#define BITSERY_BUFFER_WRITER_H
|
||||
#ifndef BITSERY_BASIC_WRITER_H
|
||||
#define BITSERY_BASIC_WRITER_H
|
||||
|
||||
#include "details/buffer_common.h"
|
||||
#include "details/sessions.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -35,18 +34,20 @@
|
||||
namespace bitsery {
|
||||
|
||||
struct MeasureSize {
|
||||
//measure class is bit-packing enabled, no need to create wrapper for it
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BITS_SIZE<T>::value;
|
||||
_bitsCount += details::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(bitsCount <= details::BITS_SIZE<T>::value);
|
||||
assert(bitsCount <= details::BitsSize<T>::value);
|
||||
_bitsCount += bitsCount;
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ namespace bitsery {
|
||||
void writeBuffer(const T *, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BITS_SIZE<T>::value * count;
|
||||
_bitsCount += details::BitsSize<T>::value * count;
|
||||
}
|
||||
|
||||
void align() {
|
||||
@@ -76,16 +77,16 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
auto endPos = getWrittenBytesCount();
|
||||
auto endPos = writtenBytesCount();
|
||||
details::writeSize(*this, endPos);
|
||||
auto sessionEndBytesCount = getWrittenBytesCount() - endPos;
|
||||
auto sessionEndBytesCount = writtenBytesCount() - endPos;
|
||||
//remove written bytes, because we'll write them at the end
|
||||
_bitsCount -= sessionEndBytesCount * 8;
|
||||
_sessionsBytesCount += sessionEndBytesCount;
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t getWrittenBytesCount() const {
|
||||
size_t writtenBytesCount() const {
|
||||
return _bitsCount / 8;
|
||||
}
|
||||
|
||||
@@ -96,29 +97,33 @@ namespace bitsery {
|
||||
|
||||
|
||||
template <typename TWriter>
|
||||
class BitPackingWriter;
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
template<typename OutputAdapter, typename Config>
|
||||
struct AdapterWriter {
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
template<typename Config, typename OutputAdapter>
|
||||
struct BasicWriter {
|
||||
using TValue = typename OutputAdapter::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit BasicWriter(OutputAdapter adapter)
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
explicit AdapterWriter(OutputAdapter&& adapter)
|
||||
: _outputAdapter{std::move(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
BasicWriter(const BasicWriter &) = delete;
|
||||
AdapterWriter(const AdapterWriter &) = delete;
|
||||
|
||||
BasicWriter &operator=(const BasicWriter &) = delete;
|
||||
AdapterWriter &operator=(const AdapterWriter &) = delete;
|
||||
|
||||
BasicWriter(BasicWriter &&) noexcept = default;
|
||||
AdapterWriter(AdapterWriter &&) noexcept = default;
|
||||
|
||||
BasicWriter &operator=(BasicWriter &&) noexcept = default;
|
||||
AdapterWriter &operator=(AdapterWriter &&) noexcept = default;
|
||||
|
||||
~BasicWriter() noexcept = default;
|
||||
~AdapterWriter() {
|
||||
flush();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
@@ -148,10 +153,11 @@ namespace bitsery {
|
||||
|
||||
void flush() {
|
||||
_session.flushSessions(*this);
|
||||
_outputAdapter.flush();
|
||||
}
|
||||
|
||||
size_t getWrittenBytesCount() const {
|
||||
return _outputAdapter.getWrittenBytesCount();
|
||||
size_t writtenBytesCount() const {
|
||||
return _outputAdapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
@@ -162,8 +168,12 @@ namespace bitsery {
|
||||
_session.end(*this);
|
||||
}
|
||||
|
||||
const OutputAdapter& adapter() const {
|
||||
return _outputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BitPackingWriter<BasicWriter<Config, OutputAdapter>>;
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
@@ -185,28 +195,35 @@ namespace bitsery {
|
||||
|
||||
OutputAdapter _outputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsWriter<BasicWriter<Config, OutputAdapter>>,
|
||||
session::DisabledSessionsWriter<BasicWriter<Config, OutputAdapter>>>::type
|
||||
session::SessionsWriter<AdapterWriter<OutputAdapter, Config >>,
|
||||
session::DisabledSessionsWriter<AdapterWriter<OutputAdapter, Config>>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
//this class is used as wrapper for real AdapterWriter, it doesn't store writer itself just a reference
|
||||
template<typename TWriter>
|
||||
struct BitPackingWriter {
|
||||
using TValue = typename TWriter::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<TValue>::type;
|
||||
class AdapterWriterBitPackingWrapper {
|
||||
public:
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
explicit BitPackingWriter(TWriter &writer)
|
||||
//make TValue unsigned for bit packing
|
||||
using UnsignedType = typename std::make_unsigned<typename TWriter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedType>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit AdapterWriterBitPackingWrapper(TWriter &writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
BitPackingWriter(const BitPackingWriter&) = delete;
|
||||
BitPackingWriter& operator = (const BitPackingWriter&) = delete;
|
||||
AdapterWriterBitPackingWrapper(const AdapterWriterBitPackingWrapper&) = delete;
|
||||
AdapterWriterBitPackingWrapper& operator = (const AdapterWriterBitPackingWrapper&) = delete;
|
||||
|
||||
BitPackingWriter(BitPackingWriter&& ) noexcept = default;
|
||||
BitPackingWriter& operator = (BitPackingWriter&& ) noexcept = default;
|
||||
AdapterWriterBitPackingWrapper(AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterWriterBitPackingWrapper& operator = (AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~BitPackingWriter() {
|
||||
~AdapterWriterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
@@ -219,7 +236,7 @@ namespace bitsery {
|
||||
_writer.template writeBytes<SIZE,T>(v);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BITS_SIZE<T>::value);
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,14 +251,14 @@ namespace bitsery {
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BITS_SIZE<T>::value);
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(0 < bitsCount && bitsCount <= details::BITS_SIZE<T>::value);
|
||||
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
|
||||
assert(v <= (bitsCount < 64
|
||||
? (1ULL << bitsCount) - 1
|
||||
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
|
||||
@@ -249,7 +266,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(TValue{}, (details::BITS_SIZE<TValue>::value - _scratchBits) % 8);
|
||||
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
@@ -257,8 +274,8 @@ namespace bitsery {
|
||||
_writer._session.flushSessions(_writer);
|
||||
}
|
||||
|
||||
size_t getWrittenBytesCount() const {
|
||||
return _writer.getWrittenBytesCount();
|
||||
size_t writtenBytesCount() const {
|
||||
return _writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
@@ -275,7 +292,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BITS_SIZE<TValue>::value;
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
@@ -283,8 +300,8 @@ namespace bitsery {
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<TValue>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(TValue), TValue >(tmp);
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
@@ -295,20 +312,20 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
//overload for TValue, for better performance
|
||||
void writeBitsInternal(const TValue &v, size_t size) {
|
||||
void writeBitsInternal(const UnsignedType &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BITS_SIZE<TValue>::value) {
|
||||
auto tmp = static_cast<TValue>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(TValue), TValue>(tmp);
|
||||
_scratch >>= details::BITS_SIZE<TValue>::value;
|
||||
_scratchBits -= details::BITS_SIZE<TValue>::value;
|
||||
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TValue _MASK = std::numeric_limits<TValue>::max();
|
||||
const UnsignedType _MASK = std::numeric_limits<UnsignedType>::max();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
TWriter& _writer;
|
||||
@@ -316,4 +333,4 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_WRITER_H
|
||||
#endif //BITSERY_BASIC_WRITER_H
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef BITSERY_BITSERY_H
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 3
|
||||
#define BITSERY_MAJOR_VERSION 4
|
||||
#define BITSERY_MINOR_VERSION 0
|
||||
#define BITSERY_PATCH_VERSION 0
|
||||
|
||||
|
||||
@@ -36,9 +36,10 @@ namespace bitsery {
|
||||
|
||||
//default configuration for buffer writing/reading operations
|
||||
struct DefaultConfig {
|
||||
//data will be stored in little endian, independant of host.
|
||||
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
|
||||
//this functionality allows to support backward/forward compatibility
|
||||
//however reading from streams is not supported, because this functionality requires random access to data buffer
|
||||
//however reading from streams is not supported, because this functionality requires random access to buffer.
|
||||
static constexpr bool BufferSessionsEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -25,35 +25,42 @@
|
||||
#define BITSERY_DESERIALIZER_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "buffer_reader.h"
|
||||
#include "adapter_reader.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename TReader, bool BitPackingEnabled = false>
|
||||
template<typename TAdapterReader>
|
||||
class BasicDeserializer {
|
||||
public:
|
||||
using BPEnabledType = BasicDeserializer<TReader, true>;
|
||||
//this is used by AdapterAccess class
|
||||
using TReader = TAdapterReader;
|
||||
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
|
||||
using BPEnabledType = BasicDeserializer<typename std::conditional<TAdapterReader::BitPackingEnabled,
|
||||
TAdapterReader, AdapterReaderBitPackingWrapper<TAdapterReader>>::type>;
|
||||
|
||||
explicit BasicDeserializer(TReader &r, void* context = nullptr)
|
||||
: _reader{r},
|
||||
|
||||
template <typename ReaderParam>
|
||||
explicit BasicDeserializer(ReaderParam&& r, void* context = nullptr)
|
||||
: _reader{std::forward<ReaderParam>(r)},
|
||||
_context{context}
|
||||
{};
|
||||
{
|
||||
};
|
||||
|
||||
//copying disabled
|
||||
BasicDeserializer(const BasicDeserializer&) = delete;
|
||||
BasicDeserializer& operator = (const BasicDeserializer&) = delete;
|
||||
|
||||
//move enabled
|
||||
BasicDeserializer(BasicDeserializer&& ) noexcept = default;
|
||||
BasicDeserializer& operator = (BasicDeserializer&& ) noexcept = default;
|
||||
BasicDeserializer(BasicDeserializer&& ) = default;
|
||||
BasicDeserializer& operator = (BasicDeserializer&& ) = default;
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific deserialization flows.
|
||||
*/
|
||||
void* getContext() {
|
||||
void* context() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
@@ -97,7 +104,7 @@ namespace bitsery {
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, !BitPackingEnabled>{});
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -136,7 +143,7 @@ namespace bitsery {
|
||||
* boolValue
|
||||
*/
|
||||
void boolValue(bool &v) {
|
||||
procBoolValue(v, std::integral_constant<bool, BitPackingEnabled>{});
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -310,11 +317,9 @@ namespace bitsery {
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
private:
|
||||
friend AdapterAccess;
|
||||
|
||||
typename std::conditional<BitPackingEnabled,
|
||||
BitPackingReader<TReader>,//by value
|
||||
TReader&//by reference
|
||||
>::type _reader;
|
||||
TAdapterReader _reader;
|
||||
void* _context;
|
||||
|
||||
//process value types
|
||||
@@ -371,7 +376,7 @@ namespace bitsery {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
@@ -379,13 +384,14 @@ namespace bitsery {
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
|
||||
BPEnabledType tmp{_reader, _context};
|
||||
fnc(tmp);
|
||||
fnc(*this);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
fnc(*this);
|
||||
//create serializer using bitpacking wrapper
|
||||
BasicDeserializer<AdapterReaderBitPackingWrapper<TAdapterReader>> tmp(_reader, _context);
|
||||
fnc(tmp);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
@@ -405,16 +411,16 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//helper type
|
||||
template <typename TReader>
|
||||
using Deserializer = BasicDeserializer<TReader, false>;
|
||||
template <typename Adapter>
|
||||
using Deserializer = BasicDeserializer<AdapterReader<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after deserialziation returns status
|
||||
template <typename Adapter, typename T, typename Config = DefaultConfig>
|
||||
std::pair<ReaderError, bool> startDeserialization(Adapter adapter, T& value) {
|
||||
BasicReader<Config, Adapter> br{std::move(adapter)};
|
||||
BasicDeserializer<BasicReader<Config, Adapter>> des{br};
|
||||
template <typename Adapter, typename T>
|
||||
std::pair<ReaderError, bool> quickDeserialization(Adapter adapter, T& value) {
|
||||
Deserializer<Adapter> des{std::move(adapter)};
|
||||
des.object(value);
|
||||
return {br.getError(), br.isCompletedSuccessfully()};
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
return {r.error(), r.isCompletedSuccessfully()};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include "both_common.h"
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
#include "../common.h"
|
||||
@@ -39,7 +39,7 @@ namespace bitsery {
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
struct BITS_SIZE:public std::integral_constant<size_t, sizeof(T) << 3> {
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
|
||||
};
|
||||
|
||||
@@ -97,37 +97,26 @@ namespace bitsery {
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct SCRATCH_TYPE {
|
||||
struct ScratchType {
|
||||
using type = NotDefinedType;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SCRATCH_TYPE<uint8_t> {
|
||||
struct ScratchType<uint8_t> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
|
||||
// template<>
|
||||
// struct SCRATCH_TYPE<uint16_t> {
|
||||
// using type = uint32_t;
|
||||
// };
|
||||
//
|
||||
// template<>
|
||||
// struct SCRATCH_TYPE<uint32_t> {
|
||||
// using type = uint64_t;
|
||||
// };
|
||||
|
||||
/*
|
||||
* class used by session reader, to access underlying iterators of buffer
|
||||
*/
|
||||
struct SessionAccess {
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& posIteratorRef(TReader& r) {
|
||||
return r._pos;
|
||||
return r.posIt;
|
||||
}
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& endIteratorRef(TReader& r) {
|
||||
return r._end;
|
||||
return r.endIt;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
namespace bitsery {
|
||||
|
||||
enum class ReaderError {
|
||||
NO_ERROR,
|
||||
DATA_OVERFLOW,
|
||||
INVALID_DATA
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData
|
||||
};
|
||||
|
||||
namespace details {
|
||||
@@ -57,7 +58,7 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.setError(ReaderError::INVALID_DATA);
|
||||
r.setError(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "both_common.h"
|
||||
#include "adapter_utils.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
@@ -39,6 +39,21 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
//serializer/deserializer, does not public interface to get underlying writer/reader
|
||||
//to prevent users from using writer/reader directly, because they have different interface
|
||||
//and they cannot be used describing serialization flows.: use extensions for this reason.
|
||||
//this class allows to get underlying adapter writer/reader, and only should be used outside serialization functions.
|
||||
struct AdapterAccess {
|
||||
template <typename Serializer>
|
||||
static typename Serializer::TWriter& getWriter(Serializer& s) {
|
||||
return s._writer;
|
||||
}
|
||||
|
||||
template <typename Deserializer>
|
||||
static typename Deserializer::TReader& getReader(Deserializer& s) {
|
||||
return s._reader;
|
||||
}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
@@ -55,6 +70,39 @@ namespace bitsery {
|
||||
struct IsExtensionTraitsDefined:public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
|
||||
};
|
||||
|
||||
//helper metafunction, that is added to c++17
|
||||
template<typename... Ts>
|
||||
struct make_void {
|
||||
typedef void type;
|
||||
};
|
||||
template<typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template <typename, typename, typename = void>
|
||||
struct HasSerializeFunction:std::false_type {};
|
||||
|
||||
template <typename S, typename T>
|
||||
struct HasSerializeFunction<S,T,
|
||||
void_t<decltype(serialize(std::declval<S &>(), std::declval<T &>()))>
|
||||
> : std::true_type {};
|
||||
|
||||
|
||||
template <typename, typename, typename = void>
|
||||
struct HasSerializeMethod:std::false_type {};
|
||||
|
||||
template <typename S, typename T>
|
||||
struct HasSerializeMethod<S,T,
|
||||
void_t<decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()))>
|
||||
> : std::true_type {};
|
||||
|
||||
template <typename, typename, typename = void>
|
||||
struct IsFlexibleIncluded:std::false_type {};
|
||||
|
||||
template <typename S, typename T>
|
||||
struct IsFlexibleIncluded<S,T,
|
||||
void_t<decltype(archiveProcess(std::declval<S &>(), std::declval<T &&>()))>
|
||||
> : std::true_type {};
|
||||
|
||||
//used for extensions, when extension TValue = void
|
||||
struct DummyType {
|
||||
};
|
||||
@@ -90,50 +138,37 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SAME_SIZE_UNSIGNED = typename UnsignedFromFundamental<T>::type;
|
||||
using SameSizeUnsigned = typename UnsignedFromFundamental<T>::type;
|
||||
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
template<typename S, typename T>
|
||||
struct SerializeFunction {
|
||||
|
||||
static void invoke(S &s, T &v) {
|
||||
static_assert(!std::is_void<Enabled>::value,
|
||||
static_assert(HasSerializeFunction<S,T>::value || HasSerializeMethod<S,T>::value,
|
||||
"\nPlease define 'serialize' function for your type (inside or outside of class):\n"
|
||||
" template<typename S>\n"
|
||||
" void serialize(S& s)\n"
|
||||
" {\n"
|
||||
" ...\n"
|
||||
" }\n");
|
||||
static_assert(!(HasSerializeFunction<S,T>::value && HasSerializeMethod<S,T>::value),
|
||||
"\nPlease define only one 'serialize' function (member OR free), not both.");
|
||||
internalInvoke(s,v, HasSerializeMethod<S,T>{});
|
||||
}
|
||||
private:
|
||||
static void internalInvoke(S& s, T& v,std::true_type) {
|
||||
Access::serialize(s,v);
|
||||
}
|
||||
static void internalInvoke(S& s, T& v,std::false_type) {
|
||||
serialize(s,v);
|
||||
}
|
||||
};
|
||||
|
||||
//check for serialize(s,o) support
|
||||
template<typename S, typename T>
|
||||
struct SerializeFunction<S, T, typename std::enable_if<
|
||||
std::is_same<void, decltype((void) serialize(std::declval<S &>(), std::declval<T &>()))>::value
|
||||
>::type> {
|
||||
|
||||
static void invoke(S &s, T &v) {
|
||||
serialize(s, v);
|
||||
}
|
||||
};
|
||||
|
||||
//check for o.serialize(s) support through static class Access
|
||||
template<typename S, typename T>
|
||||
struct SerializeFunction<S, T, typename std::enable_if<
|
||||
std::is_same<void, decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()))>::value
|
||||
>::type> {
|
||||
|
||||
static void invoke(S &s, T &v) {
|
||||
Access::serialize(s, v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
@@ -141,59 +176,16 @@ namespace bitsery {
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct ArchiveFunction {
|
||||
|
||||
static void invoke(S &s, T &v) {
|
||||
static_assert(!std::is_void<Enabled>::value,
|
||||
"\nPlease include 'flexible.h' to use 'archive' function:\n");
|
||||
}
|
||||
};
|
||||
static void invoke(S &s, T&& obj) {
|
||||
static_assert(IsFlexibleIncluded<S,T>::value,
|
||||
"\nPlease include '<bitsery/flexible.h>' to use 'archive' function:\n");
|
||||
// static_assert(HasSerializeFunction<S,T>::value || HasSerializeMethod<S,T>::value,
|
||||
// "\nPlease define 'serialize' function or include '<bitsery/flexible/...>' to use with 'archive'\n");
|
||||
|
||||
template<typename S, typename T>
|
||||
struct ArchiveFunction<S, T, typename std::enable_if<
|
||||
std::is_same<void, decltype((void)archiveProcess(std::declval<S &>(), std::declval<T &&>()))>::value
|
||||
>::type> {
|
||||
|
||||
static void invoke(S &s, T &&obj) {
|
||||
archiveProcess(s, std::forward<T>(obj));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* delta functions
|
||||
*/
|
||||
|
||||
class ObjectMemoryPosition {
|
||||
public:
|
||||
|
||||
template<typename T>
|
||||
ObjectMemoryPosition(const T &oldObj, const T &newObj)
|
||||
:ObjectMemoryPosition{reinterpret_cast<const char *>(&oldObj),
|
||||
reinterpret_cast<const char *>(&newObj),
|
||||
sizeof(T)} {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool isFieldsEquals(const T &newObjField) {
|
||||
return *getOldObjectField(newObjField) == newObjField;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T *getOldObjectField(const T &field) {
|
||||
auto offset = reinterpret_cast<const char *>(&field) - newObj;
|
||||
return reinterpret_cast<const T *>(oldObj + offset);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ObjectMemoryPosition(const char *objOld, const char *objNew, size_t)
|
||||
: oldObj{objOld},
|
||||
newObj{objNew} {
|
||||
}
|
||||
|
||||
const char *oldObj;
|
||||
const char *newObj;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include "buffer_common.h"
|
||||
#include "adapter_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
@@ -65,19 +65,19 @@ namespace bitsery {
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
*sessionIt = writer.getWrittenBytesCount();
|
||||
*sessionIt = writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto dataSize = writer.getWrittenBytesCount();
|
||||
auto dataSize = writer.writtenBytesCount();
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
auto totalSize = writer.getWrittenBytesCount();
|
||||
auto totalSize = writer.writtenBytesCount();
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 4;//4 bytes for offset data
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(sessionsOffset));
|
||||
@@ -115,7 +115,7 @@ namespace bitsery {
|
||||
if (std::distance(newEnd, _endItRef) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return;
|
||||
}
|
||||
_endItRef = newEnd;
|
||||
@@ -125,8 +125,8 @@ namespace bitsery {
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_posItRef == _endItRef || _reader.getError() == ReaderError::NO_ERROR)) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
if (!(_posItRef == _endItRef || _reader.error() == ReaderError::NoError)) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ namespace bitsery {
|
||||
//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
|
||||
//error() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_posItRef, _endItRef);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
@@ -172,7 +172,7 @@ namespace bitsery {
|
||||
auto currPos = _posItRef;
|
||||
//read size
|
||||
if (std::distance(_posItRef, _endItRef) < 4) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_endItRef, -4);
|
||||
@@ -182,7 +182,7 @@ namespace bitsery {
|
||||
|
||||
auto bufferSize = std::distance(_beginIt, _endItRef);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(ReaderError::INVALID_DATA);
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
|
||||
@@ -90,7 +90,6 @@ namespace bitsery {
|
||||
template<typename TContainer, typename T>
|
||||
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool BitPackingRequired = true;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Growable, T> {
|
||||
using TValue = T;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#ifndef BITSERY_EXT_STD_MAP_H
|
||||
#define BITSERY_EXT_STD_MAP_H
|
||||
|
||||
#include "../details/both_common.h"
|
||||
#include "../details/adapter_utils.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
@@ -71,7 +71,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdMap, T> {
|
||||
using TValue = void;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -91,7 +91,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdOptional, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -99,7 +99,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdQueue, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BITSERY_EXT_STD_SET_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../details/both_common.h"
|
||||
#include "../details/adapter_utils.h"
|
||||
//we need this, so we could
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -85,7 +85,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdSet, T> {
|
||||
using TValue = typename T::key_type;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -70,7 +70,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool BitPackingRequired = false;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define BITSERY_EXT_VALUE_RANGE_H
|
||||
|
||||
#include "../details/serialization_common.h"
|
||||
#include "../details/buffer_common.h"
|
||||
#include "../details/adapter_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
@@ -96,7 +96,7 @@ namespace bitsery {
|
||||
constexpr RangeSpec(T minValue, T maxValue, T precision) :
|
||||
min{minValue},
|
||||
max{maxValue},
|
||||
bitsRequired{calcRequiredBits<details::SAME_SIZE_UNSIGNED<T>>({}, ((max - min) / precision))} {
|
||||
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>({}, ((max - min) / precision))} {
|
||||
|
||||
}
|
||||
|
||||
@@ -106,19 +106,19 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
details::SAME_SIZE_UNSIGNED<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
return static_cast<details::SAME_SIZE_UNSIGNED<T>>(v - r.min);
|
||||
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
return static_cast<details::SameSizeUnsigned<T>>(v - r.min);
|
||||
};
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
details::SAME_SIZE_UNSIGNED<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
using VT = details::SAME_SIZE_UNSIGNED<T>;
|
||||
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
using VT = details::SameSizeUnsigned<T>;
|
||||
return static_cast<VT>(static_cast<VT>(v) - static_cast<VT>(r.min));
|
||||
};
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
details::SAME_SIZE_UNSIGNED<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
using VT = details::SAME_SIZE_UNSIGNED<T>;
|
||||
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
|
||||
using VT = details::SameSizeUnsigned<T>;
|
||||
const VT maxUint = (static_cast<VT>(1) << r.bitsRequired) - 1;
|
||||
const auto ratio = (v - r.min) / (r.max - r.min);
|
||||
return static_cast<VT>(ratio * maxUint);
|
||||
@@ -137,7 +137,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
void setRangeValue(T &v, const RangeSpec<T> &r) {
|
||||
using UIT = details::SAME_SIZE_UNSIGNED<T>;
|
||||
using UIT = details::SameSizeUnsigned<T>;
|
||||
const auto intRep = reinterpret_cast<UIT &>(v);
|
||||
const UIT maxUint = (static_cast<UIT>(1) << r.bitsRequired) - 1;
|
||||
v = r.min + (static_cast<T>(intRep) / maxUint) * (r.max - r.min);
|
||||
@@ -174,10 +174,10 @@ namespace bitsery {
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &v, Fnc &&) const {
|
||||
reader.readBits(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v), _range.bitsRequired);
|
||||
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
|
||||
details::setRangeValue(v, _range);
|
||||
if (!details::isRangeValid(v, _range)) {
|
||||
reader.setError(ReaderError::INVALID_DATA);
|
||||
reader.setError(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,6 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ValueRange<T>, T> {
|
||||
using TValue = void;
|
||||
static constexpr bool BitPackingRequired = true;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
|
||||
@@ -95,6 +95,17 @@ namespace bitsery {
|
||||
flexible::processContainer(s, obj);
|
||||
};
|
||||
|
||||
//this is a helper class that enforce fundamental type sizes, when used on multiple platforms
|
||||
template <size_t TShort, size_t TInt, size_t TLong, size_t TLongLong>
|
||||
void assertFundamentalTypeSizes() {
|
||||
//http://en.cppreference.com/w/cpp/language/types
|
||||
static_assert(sizeof(short) == TShort, "");
|
||||
static_assert(sizeof(int) == TInt, "");
|
||||
static_assert(sizeof(long) == TLong, "");
|
||||
static_assert(sizeof(long long) == TLongLong, "");
|
||||
//for completion we also need pointer type size, but serializer doesn't support pointer serialization.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_H
|
||||
|
||||
@@ -25,34 +25,40 @@
|
||||
#define BITSERY_SERIALIZER_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "buffer_writer.h"
|
||||
#include "adapter_writer.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename TWriter, bool BitPackingEnabled = false>
|
||||
template<typename TAdapterWriter>
|
||||
class BasicSerializer {
|
||||
public:
|
||||
using BPEnabledType = BasicSerializer<TWriter, true>;
|
||||
//this is used by AdapterAccess class
|
||||
using TWriter = TAdapterWriter;
|
||||
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
|
||||
using BPEnabledType = BasicSerializer<typename std::conditional<TAdapterWriter::BitPackingEnabled,
|
||||
TAdapterWriter, AdapterWriterBitPackingWrapper<TAdapterWriter>>::type>;
|
||||
|
||||
explicit BasicSerializer(TWriter &w, void* context = nullptr)
|
||||
: _writer{w},
|
||||
template <typename WriterParam>
|
||||
explicit BasicSerializer(WriterParam&& w, void* context = nullptr)
|
||||
: _writer{std::forward<WriterParam>(w)},
|
||||
_context{context}
|
||||
{};
|
||||
{
|
||||
};
|
||||
|
||||
//copying disabled
|
||||
BasicSerializer(const BasicSerializer&) = delete;
|
||||
BasicSerializer& operator = (const BasicSerializer&) = delete;
|
||||
|
||||
//move enabled
|
||||
BasicSerializer(BasicSerializer&& ) noexcept = default;
|
||||
BasicSerializer& operator = (BasicSerializer&& ) noexcept = default;
|
||||
BasicSerializer(BasicSerializer&& ) = default;
|
||||
BasicSerializer& operator = (BasicSerializer&& ) = default;
|
||||
|
||||
/*
|
||||
* get serialization context.
|
||||
* this is optional, but might be required for some specific serialization flows.
|
||||
*/
|
||||
void* getContext() {
|
||||
void* context() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
@@ -95,7 +101,7 @@ namespace bitsery {
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, !BitPackingEnabled>{});
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -135,7 +141,7 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
void boolValue(bool v) {
|
||||
procBoolValue(v, std::integral_constant<bool, BitPackingEnabled>{});
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -307,11 +313,9 @@ namespace bitsery {
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
private:
|
||||
friend AdapterAccess;
|
||||
|
||||
typename std::conditional<BitPackingEnabled,
|
||||
BitPackingWriter<TWriter>,//by value
|
||||
TWriter&//by reference
|
||||
>::type _writer;
|
||||
TAdapterWriter _writer;
|
||||
void* _context;
|
||||
|
||||
//process value types
|
||||
@@ -371,13 +375,14 @@ namespace bitsery {
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
|
||||
BPEnabledType tmp{_writer, _context};
|
||||
fnc(tmp);
|
||||
fnc(*this);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
fnc(*this);
|
||||
//create serializer using bitpacking wrapper
|
||||
BasicSerializer<AdapterWriterBitPackingWrapper<TAdapterWriter>> tmp(_writer, _context);
|
||||
fnc(tmp);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
@@ -397,26 +402,26 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
//helper type
|
||||
template <typename TWriter>
|
||||
using Serializer = BasicSerializer<TWriter, false>;
|
||||
template <typename Adapter>
|
||||
using Serializer = BasicSerializer<AdapterWriter<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
|
||||
template <typename Adapter, typename T, typename Config = DefaultConfig>
|
||||
size_t startSerialization(Adapter adapter, const T& value) {
|
||||
BasicWriter<Config, Adapter> bw{std::move(adapter)};
|
||||
BasicSerializer<BasicWriter<Config, Adapter>> ser{bw};
|
||||
template <typename Adapter, typename T>
|
||||
size_t quickSerialization(Adapter adapter, const T& value) {
|
||||
Serializer<Adapter> ser{std::move(adapter)};
|
||||
ser.object(value);
|
||||
bw.flush();
|
||||
return bw.getWrittenBytesCount();
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
size_t startMeasureSize(const T& value) {
|
||||
MeasureSize ms{};
|
||||
BasicSerializer<MeasureSize> ser {ms};
|
||||
size_t quickMeasureSize(const T& value) {
|
||||
BasicSerializer<MeasureSize> ser {nullptr};
|
||||
ser.object(value);
|
||||
ms.flush();
|
||||
return ms.getWrittenBytesCount();
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#define BITSERY_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
|
||||
#include "traits.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace bitsery {
|
||||
namespace traits {
|
||||
@@ -62,15 +63,17 @@ namespace bitsery {
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
//specialization for resizable buffers
|
||||
template <typename T>
|
||||
struct StdContainerForBufferAdapter<T, true> {
|
||||
|
||||
static void increaseBufferSize(T& container) {
|
||||
//use default implementation behaviour;
|
||||
//call push_back to use default resize strategy
|
||||
container.push_back({});
|
||||
//after allocation resize to take all capacity
|
||||
container.resize(container.capacity());
|
||||
//since we're writing to buffer use different resize strategy than default implementation
|
||||
//when small size grow faster, to avoid thouse 2/4/8/16... byte allocations
|
||||
auto newSize = static_cast<size_t>(container.size() * 1.5 + 128);
|
||||
//make data cache friendly
|
||||
newSize -= newSize % 64;//64 is cache line size
|
||||
container.resize(std::max(newSize, container.capacity()));
|
||||
}
|
||||
using TIterator = typename T::iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
|
||||
@@ -52,7 +52,7 @@ include(${ExtCMakeFilesDir}/LinkTestLib.cmake)
|
||||
FOREACH(TestFile ${TestSourceFiles})
|
||||
get_filename_component(TestName ${TestFile} NAME_WE)
|
||||
set(TestName TEST_${TestName})
|
||||
add_executable(${TestName} ${TestFile} ${IncludeHeaders})
|
||||
add_executable(${TestName} ${TestFile} ${IncludeHeaders} serialization_test_utils.h)
|
||||
LinkTestLib(${TestName})
|
||||
|
||||
add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
@@ -50,7 +50,7 @@ struct IntegralTypes {
|
||||
int8_t e;
|
||||
};
|
||||
|
||||
using InverseReader = bitsery::BasicReader<InverseEndiannessConfig, InputAdapter >;
|
||||
using InverseReader = bitsery::AdapterReader<InputAdapter, InverseEndiannessConfig>;
|
||||
|
||||
|
||||
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
@@ -80,7 +80,7 @@ TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
bw.writeBytes<1>(src.e);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<4>(res.b);
|
||||
@@ -106,7 +106,7 @@ TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
|
||||
bw.writeBuffer<1>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.readBuffer<1>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
@@ -125,7 +125,7 @@ TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
bw.writeBuffer<2>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.readBuffer<2>(res, SIZE);
|
||||
//result is identical, because we write separate values, of size 1byte, that requires no swapping
|
||||
//check results
|
||||
@@ -148,10 +148,10 @@ struct IntegralUnsignedTypes {
|
||||
TEST(DataEndianness, WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
|
||||
//fill initial values
|
||||
constexpr IntegralUnsignedTypes src {
|
||||
0x0000334455667788,//bits 19
|
||||
0x00CCDDEE,//bits 16
|
||||
0x00DD,//bits 1
|
||||
0x0F,//bits 6
|
||||
0x0000334455667788,
|
||||
0x00CCDDEE,
|
||||
0x00DD,
|
||||
0x0F,
|
||||
};
|
||||
|
||||
constexpr size_t aBITS = getBits(src.a) + 8;
|
||||
@@ -161,15 +161,15 @@ TEST(DataEndianness, WhenValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndiann
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
bitsery::BitPackingWriter<Writer> bpw{bw};
|
||||
bitsery::AdapterWriterBitPackingWrapper<Writer> bpw{bw};
|
||||
bpw.writeBits(src.a, aBITS);
|
||||
bpw.writeBits(src.b, bBITS);
|
||||
bpw.writeBits(src.c, cBITS);
|
||||
bpw.writeBits(src.d, dBITS);
|
||||
bpw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
InverseReader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<InverseReader> bpr{br};
|
||||
InverseReader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<InverseReader> bpr{br};
|
||||
IntegralUnsignedTypes res{};
|
||||
bpr.readBits(res.a, aBITS);
|
||||
bpr.readBits(res.b, bBITS);
|
||||
@@ -28,8 +28,8 @@
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
using BitPackingWriter = bitsery::BitPackingWriter<Writer>;
|
||||
using BitPackingReader = bitsery::BitPackingReader<Reader>;
|
||||
using AdapterBitPackingWriter = bitsery::AdapterWriterBitPackingWrapper<Writer>;
|
||||
using AdapterBitPackingReader = bitsery::AdapterReaderBitPackingWrapper<Reader>;
|
||||
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
@@ -50,15 +50,15 @@ constexpr size_t getBits(T v) {
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
bpw.writeBits(std::numeric_limits<uint64_t>::max(), 64);
|
||||
bpw.writeBits(std::numeric_limits<uint32_t>::max(), 32);
|
||||
bpw.writeBits(std::numeric_limits<uint16_t>::max(), 16);
|
||||
bpw.writeBits(std::numeric_limits<uint8_t>::max(), 8);
|
||||
bpw.flush();
|
||||
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint64_t v64{};
|
||||
uint32_t v32{};
|
||||
uint16_t v16{};
|
||||
@@ -93,7 +93,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(data.a, aBITS);
|
||||
bpw.writeBits(data.b, bBITS);
|
||||
@@ -101,12 +101,12 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
|
||||
bpw.writeBits(data.d, dBITS);
|
||||
bpw.writeBits(data.e, eBITS);
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
|
||||
EXPECT_THAT(writtenSize, Eq(bytesCount));
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr{br};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
|
||||
IntegralUnsignedTypes res{};
|
||||
|
||||
@@ -130,43 +130,43 @@ TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(7u,3);
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr{br};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint16_t tmp;
|
||||
bpr.readBits(tmp,4);
|
||||
bpr.readBits(tmp,2);
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//part of next byte
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
bpr1.readBits(tmp,7);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//bigger than byte
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr2{br2};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
bpr2.readBits(tmp,9);
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));//false
|
||||
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(3u, 2);
|
||||
//3 calls to align after 1st data
|
||||
@@ -180,22 +180,22 @@ TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
bpw.flush();
|
||||
|
||||
unsigned char tmp;
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(tmp, Eq(3u));
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
bpr.align();
|
||||
bpr.align();
|
||||
EXPECT_THAT(tmp, Eq(7u));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
|
||||
bpr.readBits(tmp,4);
|
||||
EXPECT_THAT(tmp, Eq(15u));
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
@@ -204,28 +204,28 @@ TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
//write 2 bits and align
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
unsigned char tmp;
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
//read aligned bits
|
||||
bpr1.readBits(tmp,6);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr2{br2};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
//read 2 bits
|
||||
bpr2.readBits(tmp,2);
|
||||
bpr2.align();
|
||||
EXPECT_THAT(bpr2.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::NoError));
|
||||
}
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
bw.writeBytes<1>(data.e);
|
||||
bw.writeBuffer<1>(data.f, 2);
|
||||
bw.flush();
|
||||
auto writtenSize = bw.getWrittenBytesCount();
|
||||
auto writtenSize = bw.writtenBytesCount();
|
||||
|
||||
EXPECT_THAT(writtenSize, Eq(18));
|
||||
//read from buffer
|
||||
@@ -273,7 +273,7 @@ TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<1>(res.e);
|
||||
br.readBuffer<1>(res.f, 2);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
//assert results
|
||||
|
||||
EXPECT_THAT(data.a, Eq(res.a));
|
||||
@@ -295,10 +295,10 @@ TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
}
|
||||
|
||||
@@ -309,23 +309,23 @@ TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
bpw.writeBits(15u, 4);
|
||||
bpw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bpw.writeBits(12u, 4);
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.getWrittenBytesCount();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(sizeof(src) + 1));
|
||||
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
BitPackingReader bpr1{br1};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
uint8_t tmp{};
|
||||
bpr1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(15));
|
||||
bpr1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(bpr1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
bpr1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(12));
|
||||
@@ -338,7 +338,7 @@ TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZ
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
BitPackingWriter bpw{bw};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
bpw.writeBits(2u, 2);
|
||||
bpw.writeBytes<2>(data[0]);
|
||||
bpw.writeBytes<2>(data[1]);
|
||||
@@ -346,8 +346,8 @@ TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZ
|
||||
bpw.flush();
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bpw.getWrittenBytesCount()}};
|
||||
BitPackingReader bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint8_t tmp{};
|
||||
bpr.readBits(tmp, 2);
|
||||
EXPECT_THAT(tmp, Eq(2));
|
||||
@@ -50,10 +50,10 @@ TEST(DataReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
TEST(DataReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
@@ -69,12 +69,12 @@ TEST(DataReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
br.readBytes<1>(a);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
|
||||
@@ -94,31 +94,31 @@ TEST(DataReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
IntegralTypes res;
|
||||
br.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(true));
|
||||
br.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
|
||||
Reader br1{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br1.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
br1.readBytes<1>(res.d);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
}
|
||||
|
||||
@@ -135,11 +135,11 @@ TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<Reader> bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
int32_t c;
|
||||
bpr.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::DATA_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
|
||||
int16_t r1= {-645};
|
||||
uint32_t r2[2] = {54898,87854};
|
||||
@@ -25,29 +25,29 @@
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using testing::Eq;
|
||||
using SessionsEnabledWriter = bitsery::BasicWriter<SessionsEnabledConfig, OutputAdapter>;
|
||||
using SessionsEnabledReader = bitsery::BasicReader<SessionsEnabledConfig, InputAdapter>;
|
||||
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;
|
||||
using SessionsEnabledReader = bitsery::AdapterReader<InputAdapter, SessionsEnabledConfig>;
|
||||
|
||||
TEST(DataReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidDataError) {
|
||||
SerializationContext ctx;
|
||||
std::string tmp = "larger text then allowed";
|
||||
ctx.createSerializer().text1b(tmp,100);
|
||||
ctx.createDeserializer().text1b(tmp, 10);
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) {
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value1b(uint8_t{1});
|
||||
ser.value1b(uint8_t{2});
|
||||
bool res{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(true));
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(false));
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingAlignHasNonZerosThenInvalidDataError) {
|
||||
@@ -57,12 +57,12 @@ TEST(DataReadingErrors, WhenReadingAlignHasNonZerosThenInvalidDataError) {
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.flush();
|
||||
|
||||
Reader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
bitsery::BitPackingReader<Reader> bpr{br};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataError) {
|
||||
@@ -76,7 +76,7 @@ TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataErr
|
||||
bw.endSession();
|
||||
}
|
||||
bw.flush();
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<1>(tmp);
|
||||
@@ -85,7 +85,7 @@ TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataErr
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
}
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
@@ -95,18 +95,18 @@ TEST(DataReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidData
|
||||
SessionsEnabledWriter bw1{buf1};
|
||||
bw1.writeBytes<1>(tmp1);
|
||||
bw1.flush();
|
||||
SessionsEnabledReader br1{InputAdapter{buf1.begin(), bw1.getWrittenBytesCount()}};
|
||||
SessionsEnabledReader br1{InputAdapter{buf1.begin(), bw1.writtenBytesCount()}};
|
||||
br1.beginSession();
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
|
||||
Buffer buf2{};
|
||||
SessionsEnabledWriter bw2{buf2};
|
||||
uint16_t tmp2{0x8000};
|
||||
bw2.writeBytes<2>(tmp2);
|
||||
bw2.flush();
|
||||
SessionsEnabledReader br2{InputAdapter{buf2.begin(), bw2.getWrittenBytesCount()}};
|
||||
SessionsEnabledReader br2{InputAdapter{buf2.begin(), bw2.writtenBytesCount()}};
|
||||
br2.beginSession();
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(br2.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidData) {
|
||||
@@ -115,7 +115,7 @@ TEST(DataReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorrupt
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<2>(uint16_t{10});
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.getWrittenBytesCount()}};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.beginSession();
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::ReaderError::INVALID_DATA));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
@@ -32,7 +32,7 @@ using bitsery::EndiannessType;
|
||||
template <typename BufType>
|
||||
class DataWriting:public testing::Test {
|
||||
public:
|
||||
using TWriter = bitsery::BasicWriter<bitsery::DefaultConfig, bitsery::OutputBufferAdapter<BufType>>;
|
||||
using TWriter = bitsery::AdapterWriter<bitsery::OutputBufferAdapter<BufType>, bitsery::DefaultConfig>;
|
||||
using TBuffer = BufType;
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten) {
|
||||
TWriter bw{buf};
|
||||
writeData(bw);
|
||||
bw.flush();
|
||||
auto writtenSize = bw.getWrittenBytesCount();
|
||||
auto writtenSize = bw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, DATA_SIZE);
|
||||
EXPECT_THAT(buf.size(), ::testing::Ge(DATA_SIZE));
|
||||
}
|
||||
@@ -73,11 +73,11 @@ TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::BitPackingWriter<TWriter> bpw{bw};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
auto writtenSize1 = bpw.getWrittenBytesCount();
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
bpw.flush();
|
||||
auto writtenSize2 = bpw.getWrittenBytesCount();
|
||||
auto writtenSize2 = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(0));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
}
|
||||
@@ -87,12 +87,12 @@ TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::BitPackingWriter<TWriter> bpw{bw};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
auto writtenSize1 = bpw.getWrittenBytesCount();
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
bpw.flush();
|
||||
auto writtenSize2 = bpw.getWrittenBytesCount();
|
||||
auto writtenSize2 = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(1));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
|
||||
@@ -100,7 +100,7 @@ TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
|
||||
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity) {
|
||||
NonFixedContainer buf{};
|
||||
bitsery::BasicWriter<bitsery::DefaultConfig, bitsery::OutputBufferAdapter<NonFixedContainer>> bw{buf};
|
||||
bitsery::AdapterWriter<bitsery::OutputBufferAdapter<NonFixedContainer>, bitsery::DefaultConfig> bw{buf};
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
uint32_t tmp{};
|
||||
bw.writeBytes<4>(tmp);
|
||||
@@ -72,7 +72,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
double_t td = -454184.48445;
|
||||
bool tb=true;
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(ti);
|
||||
ser.object(te);
|
||||
ser.object(tf);
|
||||
@@ -85,7 +85,7 @@ TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
float rf{};
|
||||
double_t rd{};
|
||||
bool rb{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(ri);
|
||||
des.object(re);
|
||||
des.object(rf);
|
||||
@@ -107,7 +107,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
double_t td = -454184.48445;
|
||||
bool tb=true;
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value<sizeof(ti)>(ti);
|
||||
ser.archive(te, tf, td);
|
||||
ser.object(tb);
|
||||
@@ -118,7 +118,7 @@ TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
float rf{};
|
||||
double_t rd{};
|
||||
bool rb{};
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.archive(ri, re, rf);
|
||||
des.value8b(rd);
|
||||
des.object(rb);
|
||||
|
||||
@@ -26,11 +26,9 @@
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
template <bool BitPackingEnabled>
|
||||
using Serializer = bitsery::BasicSerializer<Writer, BitPackingEnabled>;
|
||||
using Serializer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
|
||||
template <bool BitPackingEnabled>
|
||||
using Deserializer = bitsery::BasicDeserializer<Reader, BitPackingEnabled>;
|
||||
using Deserializer = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
|
||||
|
||||
TEST(SerializeBooleans, BoolAsBit) {
|
||||
@@ -40,13 +38,13 @@ TEST(SerializeBooleans, BoolAsBit) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.enableBitPacking([&t1, &t2](Serializer<true>& sbp) {
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.enableBitPacking([&t1, &t2](Serializer& sbp) {
|
||||
sbp.boolValue(t1);
|
||||
sbp.boolValue(t2);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
des.enableBitPacking([&res1, &res2](Deserializer <true>& sbp) {
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.enableBitPacking([&res1, &res2](Deserializer& sbp) {
|
||||
sbp.boolValue(res1);
|
||||
sbp.boolValue(res2);
|
||||
});
|
||||
@@ -62,10 +60,10 @@ TEST(SerializeBooleans, BoolAsByte) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.boolValue(t1);
|
||||
ser.boolValue(t2);
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res1);
|
||||
des.boolValue(res2);
|
||||
|
||||
|
||||
@@ -108,11 +108,11 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements
|
||||
SerializationContext ctx{};
|
||||
using TValue = typename TestFixture::TValue;
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(this->src, 1000, [&ser](TValue& v) {
|
||||
ser.template value<sizeof(v)>(v);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(this->res, 1000, [&des](TValue &v) {
|
||||
des.template value<sizeof(v)>(v);
|
||||
//increment by 1 after reading
|
||||
@@ -233,13 +233,13 @@ TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, CustomFunctionThatSerializ
|
||||
using TValue = decltype(*std::begin(res));
|
||||
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.container(src, [&ser](TValue &v) {
|
||||
char tmp{};
|
||||
ser.object(v);
|
||||
ser.value1b(tmp);
|
||||
});
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.container(res, [&des](TValue &v) {
|
||||
char tmp{};
|
||||
des.object(v);
|
||||
|
||||
@@ -30,22 +30,33 @@ using namespace testing;
|
||||
|
||||
using bitsery::ext::Entropy;
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
using BPDes = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
|
||||
|
||||
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenOnlyWriteIndexUsingMinRequiredBits) {
|
||||
int32_t v = 4849;
|
||||
int32_t res;
|
||||
constexpr size_t N = 3;
|
||||
int32_t values[3] = {485,4849,89};
|
||||
SerializationContext ctx;
|
||||
ctx.createBPEnabledSerializer().ext4b(v, Entropy<int32_t[3]>{values});
|
||||
ctx.createBPEnabledDeserializer().ext4b(res, Entropy<int32_t[3]>{values});
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext4b(v, Entropy<int32_t[3]>{values});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext4b(res, Entropy<int32_t[3]>{values});
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
|
||||
SerializationContext ctx1;
|
||||
ctx1.createBPEnabledSerializer().ext4b(v, Entropy<int32_t[3]>{values});
|
||||
auto des = ctx1.createBPEnabledDeserializer();
|
||||
des.ext(res, bitsery::ext::ValueRange<int32_t>{0, static_cast<int32_t>(N + 1)});
|
||||
ctx1.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext4b(v, Entropy<int32_t[3]>{values});
|
||||
});
|
||||
ctx1.createDeserializer().enableBitPacking([&res](BPDes& des) {
|
||||
des.ext(res, bitsery::ext::ValueRange<int32_t>{0, static_cast<int32_t>(N + 1)});
|
||||
});
|
||||
EXPECT_THAT(res, Eq(2));
|
||||
}
|
||||
|
||||
@@ -54,8 +65,12 @@ TEST(SerializeExtensionEntropy, WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrO
|
||||
int16_t res;
|
||||
std::initializer_list<int> values{485,4849,89};
|
||||
SerializationContext ctx;
|
||||
ctx.createBPEnabledSerializer().ext2b(v, Entropy<std::initializer_list<int>>{values});
|
||||
ctx.createBPEnabledDeserializer().ext2b(res, Entropy<std::initializer_list<int>>{values});
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext2b(v, Entropy<std::initializer_list<int>>{values});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext2b(res, Entropy<std::initializer_list<int>>{values});
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(sizeof(int16_t)+1));
|
||||
@@ -70,9 +85,12 @@ TEST(SerializeExtensionEntropy, CustomTypeEntropyEncoded) {
|
||||
MyStruct1{12, 10}, MyStruct1{485, 454},
|
||||
MyStruct1{4849, 89}, MyStruct1{0, 1}};
|
||||
SerializationContext ctx;
|
||||
ctx.createBPEnabledSerializer().ext(v, Entropy<MyStruct1[N]>{values});
|
||||
ctx.createBPEnabledDeserializer().ext(res, Entropy<MyStruct1[N]>{values});
|
||||
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext(v, Entropy<MyStruct1[N]>{values});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext(res, Entropy<MyStruct1[N]>{values});
|
||||
});
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
}
|
||||
@@ -85,9 +103,12 @@ TEST(SerializeExtensionEntropy, CustomTypeNotEntropyEncoded) {
|
||||
MyStruct1{12,10}, MyStruct1{485, 454},
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
SerializationContext ctx;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(v, Entropy<std::initializer_list<MyStruct1>>{values});
|
||||
ctx.createBPEnabledDeserializer().ext(res, Entropy<std::initializer_list<MyStruct1>>{values});
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext(v, Entropy<std::initializer_list<MyStruct1>>{values});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext(res, Entropy<std::initializer_list<MyStruct1>>{values});
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(MyStruct1::SIZE + 1));
|
||||
@@ -105,21 +126,22 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBefore
|
||||
auto rangeForValue = bitsery::ext::ValueRange<int>{0, 10000};
|
||||
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createBPEnabledSerializer();
|
||||
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, false), serLambda);
|
||||
});
|
||||
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, false), serLambda);
|
||||
|
||||
auto des = ctx.createBPEnabledDeserializer();
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
des.ext(res, Entropy<std::vector<MyStruct1>>(values, false), desLambda);
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
des.ext(res, Entropy<std::vector<MyStruct1>>(values, false), desLambda);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
auto rangeForIndex = bitsery::ext::ValueRange<size_t>{0u, N+1};
|
||||
@@ -137,21 +159,21 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeDa
|
||||
auto rangeForValue = bitsery::ext::ValueRange<int>{0, 10000};
|
||||
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createBPEnabledSerializer();
|
||||
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, true), serLambda);
|
||||
|
||||
auto des = ctx.createBPEnabledDeserializer();
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
des.ext(res, Entropy<std::vector<MyStruct1>>(values, true), desLambda);
|
||||
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
ser.ext(data.i1, rangeForValue);
|
||||
ser.ext(data.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values, true), serLambda);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values, &rangeForValue](BPDes& des) {
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& data) {
|
||||
des.ext(data.i1, rangeForValue);
|
||||
des.ext(data.i2, rangeForValue);
|
||||
};
|
||||
des.ext(res, Entropy<std::vector<MyStruct1>>(values, true), desLambda);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
auto bitsForIndex = 8; //because aligned
|
||||
@@ -166,8 +188,12 @@ TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked)
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createBPEnabledSerializer().ext(v, Entropy<std::list<MyStruct1>>{values}, [](MyStruct1& ) {});
|
||||
ctx.createBPEnabledDeserializer().ext(res, Entropy<std::list<MyStruct1>>{values}, []( MyStruct1& ) {});
|
||||
ctx.createSerializer().enableBitPacking([&v, &values](BPSer& ser) {
|
||||
ser.ext(v, Entropy<std::list<MyStruct1>>{values}, [](MyStruct1& ) {});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res, &values](BPDes& des) {
|
||||
des.ext(res, Entropy<std::list<MyStruct1>>{values}, []( MyStruct1& ) {});
|
||||
});
|
||||
|
||||
EXPECT_THAT(res, Eq(v));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
|
||||
@@ -60,7 +60,7 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSess
|
||||
constexpr size_t DATA_SIZE = 4;
|
||||
int32_t data{};
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.ext(data, Growable{}, [&ser](int32_t & v) { ser.value4b(v);});
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
|
||||
@@ -78,7 +78,7 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSess
|
||||
uint32_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<4>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+4));//1byte for session info, 4 bytes for session offset variable
|
||||
auto writtenSize = ctx.bw->getWrittenBytesCount();
|
||||
auto writtenSize = ctx.bw->writtenBytesCount();
|
||||
auto dSize = writtenSize - sessionsOffset;
|
||||
EXPECT_THAT(dSize, Eq(DATA_SIZE));
|
||||
}
|
||||
|
||||
@@ -26,19 +26,17 @@
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
|
||||
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
|
||||
#include<optional>
|
||||
|
||||
#include <optional>
|
||||
#include <bitsery/ext/std_optional.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
|
||||
using StdOptional = bitsery::ext::StdOptional;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<Writer, true>;
|
||||
using BPDes = bitsery::BasicDeserializer<Reader, true>;
|
||||
|
||||
template <typename T>
|
||||
void test(SerializationContext& ctx, const T& v, T& r) {
|
||||
@@ -83,16 +81,19 @@ TEST(SerializeExtensionStdOptional, OptionalHasValue) {
|
||||
TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead) {
|
||||
std::optional<int32_t> t1{43};
|
||||
std::optional<int32_t> r1{52};
|
||||
auto range = bitsery::ext::ValueRange<int>{40,60};
|
||||
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createBPEnabledSerializer();
|
||||
auto range = bitsery::ext::ValueRange<int>{40,60};
|
||||
ser.ext(t1, StdOptional(true), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
|
||||
ser.ext(t1, StdOptional(true), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
auto des = ctx.createBPEnabledDeserializer();
|
||||
des.ext(r1, StdOptional(true), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(true), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(2));//1byte for index + 1byte for value
|
||||
@@ -102,17 +103,21 @@ TEST(SerializeExtensionStdOptional, AlignAfterStateWriteRead) {
|
||||
TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
|
||||
std::optional<int32_t> t1{43};
|
||||
std::optional<int32_t> r1{52};
|
||||
auto range = bitsery::ext::ValueRange<int>{40,60};
|
||||
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createBPEnabledSerializer();
|
||||
auto range = bitsery::ext::ValueRange<int>{40,60};
|
||||
ser.ext(t1, StdOptional(false), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
ser.ext(t1, StdOptional(false), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
auto des = ctx.createBPEnabledDeserializer();
|
||||
des.ext(r1, StdOptional(false), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(false), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
EXPECT_THAT(range.getRequiredBits() + 1, ::testing::Lt(8));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
@@ -66,11 +66,11 @@ TEST(SerializeExtensionStdSet, FunctionSyntax) {
|
||||
SerializationContext ctx1;
|
||||
std::unordered_multiset<int32_t> t1{54,-484,841,79};
|
||||
std::unordered_multiset<int32_t> r1{};
|
||||
auto ser = ctx1.createSerializer();
|
||||
auto& ser = ctx1.createSerializer();
|
||||
ser.ext(t1, StdSet{10}, [&ser](int32_t& v) {
|
||||
ser.value4b(v);
|
||||
});
|
||||
auto des = ctx1.createDeserializer();
|
||||
auto& des = ctx1.createDeserializer();
|
||||
des.ext(r1, StdSet{10}, [&des](int32_t& v) {
|
||||
des.value4b(v);
|
||||
});
|
||||
|
||||
@@ -49,14 +49,21 @@ TEST(SerializeExtensionValueRange, RequiredBitsIsConstexpr) {
|
||||
|
||||
#endif
|
||||
|
||||
using BPSer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
using BPDes = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
|
||||
|
||||
TEST(SerializeExtensionValueRange, IntegerNegative) {
|
||||
SerializationContext ctx;
|
||||
ValueRange<int> r1{-50, 50};
|
||||
int t1{-8};
|
||||
int res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, Eq(t1));
|
||||
@@ -69,8 +76,12 @@ TEST(SerializeExtensionValueRange, IntegerPositive) {
|
||||
unsigned t1{8};
|
||||
unsigned res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, Eq(t1));
|
||||
@@ -83,8 +94,12 @@ TEST(SerializeExtensionValueRange, EnumTypes) {
|
||||
MyEnumClass t1{MyEnumClass::E2};
|
||||
MyEnumClass res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, Eq(t1));
|
||||
@@ -101,8 +116,12 @@ TEST(SerializeExtensionValueRange, FloatUsingPrecisionConstraint1) {
|
||||
|
||||
float res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, ::testing::FloatNear(t1, (max - min) * precision));
|
||||
@@ -118,8 +137,12 @@ TEST(SerializeExtensionValueRange, DoubleUsingPrecisionConstraint2) {
|
||||
|
||||
double res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(5));
|
||||
EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) * precision));
|
||||
@@ -135,11 +158,15 @@ TEST(SerializeExtensionValueRange, FloatUsingBitsSizeConstraint1) {
|
||||
|
||||
float res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, ::testing::FloatNear(t1, (max - min) / (static_cast<bitsery::details::SAME_SIZE_UNSIGNED<float>>(1) << bits)));
|
||||
EXPECT_THAT(res1, ::testing::FloatNear(t1, (max - min) / (static_cast<bitsery::details::SameSizeUnsigned<float>>(1) << bits)));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionValueRange, DoubleUsingBitsSizeConstraint2) {
|
||||
@@ -152,11 +179,15 @@ TEST(SerializeExtensionValueRange, DoubleUsingBitsSizeConstraint2) {
|
||||
|
||||
double res1;
|
||||
|
||||
ctx.createBPEnabledSerializer().ext(t1, r1);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
ctx.createSerializer().enableBitPacking([&t1, &r1](BPSer& ser) {
|
||||
ser.ext(t1, r1);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(7));
|
||||
EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) / (static_cast<bitsery::details::SAME_SIZE_UNSIGNED<double>>(1) << bits)));
|
||||
EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) / (static_cast<bitsery::details::SameSizeUnsigned<double>>(1) << bits)));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionValueRange, WhenDataIsInvalidThenReturnMinimumRangeValue) {
|
||||
@@ -164,8 +195,13 @@ TEST(SerializeExtensionValueRange, WhenDataIsInvalidThenReturnMinimumRangeValue)
|
||||
ValueRange<int> 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.createBPEnabledSerializer().value1b(tmp);
|
||||
ctx.createBPEnabledDeserializer().ext(res1, r1);
|
||||
|
||||
ctx.createSerializer().enableBitPacking([&tmp](BPSer& ser) {
|
||||
ser.value1b(tmp);
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&res1, &r1](BPDes& des) {
|
||||
des.ext(res1, r1);
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(res1, Eq(4));
|
||||
|
||||
@@ -98,7 +98,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
z.x = X{ 234 };
|
||||
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(y);
|
||||
ser.object(z);
|
||||
|
||||
@@ -106,7 +106,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
Y yres{};
|
||||
Z zres{};
|
||||
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(yres);
|
||||
des.object(zres);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <memory>
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/adapters/buffer_adapters.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
|
||||
/*
|
||||
* define some types for testing
|
||||
@@ -90,37 +90,44 @@ struct SessionsEnabledConfig: public bitsery::DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using Buffer = std::vector<char>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::BasicWriter<bitsery::DefaultConfig, OutputAdapter>;
|
||||
using Reader = bitsery::BasicReader<bitsery::DefaultConfig, InputAdapter>;
|
||||
using Writer = bitsery::AdapterWriter<OutputAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
|
||||
template <typename Config = bitsery::DefaultConfig>
|
||||
class BasicSerializationContext {
|
||||
public:
|
||||
Buffer buf;
|
||||
std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>> bw;
|
||||
std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>> br;
|
||||
std::unique_ptr<bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>> sbp;
|
||||
using TWriter = bitsery::AdapterWriter<OutputAdapter, Config>;
|
||||
using TReader = bitsery::AdapterReader<InputAdapter, Config>;
|
||||
std::unique_ptr<bitsery::BasicSerializer<TWriter>> ser;
|
||||
std::unique_ptr<bitsery::BasicDeserializer<TReader>> des;
|
||||
TWriter* bw;
|
||||
TReader* br;
|
||||
|
||||
bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, false> createSerializer() {
|
||||
//make_unique is not in c++11
|
||||
bw = std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>>(new bitsery::BasicWriter<Config, OutputAdapter>(buf));
|
||||
return bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, false>{*bw};
|
||||
bitsery::BasicSerializer<TWriter>& createSerializer() {
|
||||
if (!ser) {
|
||||
ser = std::unique_ptr<bitsery::BasicSerializer<TWriter>>(new bitsery::BasicSerializer<TWriter>(OutputAdapter{buf}));
|
||||
bw = &bitsery::AdapterAccess::getWriter(*ser);
|
||||
}
|
||||
return *ser;
|
||||
};
|
||||
|
||||
bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>& createBPEnabledSerializer() {
|
||||
//make_unique is not in c++11
|
||||
bw = std::unique_ptr<bitsery::BasicWriter<Config, OutputAdapter>>(new bitsery::BasicWriter<Config, OutputAdapter>(buf));
|
||||
sbp = std::unique_ptr<bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>>(
|
||||
new bitsery::BasicSerializer<bitsery::BasicWriter<Config, OutputAdapter>, true>{*bw});
|
||||
return *sbp;
|
||||
bitsery::BasicDeserializer<bitsery::AdapterReader<InputAdapter, Config>>& createDeserializer() {
|
||||
bw->flush();
|
||||
if (!des) {
|
||||
des = std::unique_ptr<bitsery::BasicDeserializer<TReader>>(
|
||||
new bitsery::BasicDeserializer<TReader>(InputAdapter{buf.begin(), bw->writtenBytesCount()}));
|
||||
br = &bitsery::AdapterAccess::getReader(*des);
|
||||
}
|
||||
return *des;
|
||||
};
|
||||
|
||||
size_t getBufferSize() const {
|
||||
return bw->getWrittenBytesCount();
|
||||
return bw->writtenBytesCount();
|
||||
}
|
||||
|
||||
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
|
||||
@@ -133,21 +140,6 @@ public:
|
||||
return 4;
|
||||
}
|
||||
|
||||
bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, false> createDeserializer() {
|
||||
bw->flush();
|
||||
//make_unique is not in c++11
|
||||
br = std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>>(
|
||||
new bitsery::BasicReader<Config, InputAdapter>(InputAdapter{buf.begin(), bw->getWrittenBytesCount()}));
|
||||
return bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, false>{*br};
|
||||
};
|
||||
|
||||
bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, true> createBPEnabledDeserializer() {
|
||||
sbp.reset(nullptr);
|
||||
//make_unique is not in c++11
|
||||
br = std::unique_ptr<bitsery::BasicReader<Config, InputAdapter>>(
|
||||
new bitsery::BasicReader<Config, InputAdapter>(InputAdapter{buf.begin(), bw->getWrittenBytesCount()}));
|
||||
return bitsery::BasicDeserializer<bitsery::BasicReader<Config, InputAdapter>, true>{*br};
|
||||
};
|
||||
};
|
||||
|
||||
//helper type
|
||||
|
||||
Reference in New Issue
Block a user