mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-18 13:19:11 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ccd4d368e | ||
|
|
f3c9a33849 | ||
|
|
5ede853954 | ||
|
|
982374be42 | ||
|
|
ab5cc8a2c0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.idea/
|
||||
.vs/
|
||||
build/
|
||||
cmake-build-debug/
|
||||
|
||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,3 +1,34 @@
|
||||
# [4.0.0](https://github.com/fraillt/bitsery/compare/v3.0.0...v4.0.0) (2017-10-13)
|
||||
|
||||
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
|
||||
|
||||
* 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)
|
||||
|
||||
### Features
|
||||
@@ -51,7 +82,7 @@
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
64
README.md
64
README.md
@@ -14,65 +14,75 @@ 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.
|
||||
|
||||
## Why to use bitsery
|
||||
|
||||
Look at the numbers and features list, and decide yourself. *(benchmarked on Ubuntu with GCC 7.1)*
|
||||
Look at the numbers and features list, and decide yourself.
|
||||
|
||||
| | serialize | deserialize | data size | executable size |
|
||||
|-----------------------------------------------------------|-----------|-------------|-------------|-----------------|
|
||||
| flatbuffers | 1852 ms. | 777 ms. | 27252 bytes | 74544 bytes |
|
||||
| cereal | 1069 ms. | 1385 ms. | 20208 bytes | 72336 bytes |
|
||||
| bitsery | 808 ms. | 737 ms. | 14803 bytes | 69784 bytes |
|
||||
| bitsery fixed-size buffer | 297 ms. | 738 ms. | 14803 bytes | 69928 bytes |
|
||||
| bitsery optimized serialization flow | 686 ms. | 997 ms. | 6601 bytes | 69320 bytes |
|
||||
| bitsery optimized serialization flow + fixed-size buffer | 446 ms. | 996 ms. | 6601 bytes | 69464 bytes |
|
||||
| | binary size | data size | serialize | deserialize |
|
||||
|------------------------------|-------------|-----------|-------------|-------------|
|
||||
| **test_bitsery** | 64704 | **7565** | **1229 ms** | **1086 ms** |
|
||||
| **test_bitsery_compression** | 44000 | **4784** | **1370 ms** | **2463 ms** |
|
||||
| test_yas | 63864 | 11311 | 1616 ms | 1712 ms |
|
||||
| test_yas_compression | 72688 | 8523 | 2387 ms | 2890 ms |
|
||||
| test_cereal | 74848 | 11261 | 6708 ms | 6799 ms |
|
||||
| test_flatbuffers | 67032 | 16100 | 8793 ms | 3028 ms |
|
||||
|
||||
*benchmarked on Ubuntu with GCC 7.1.0, more details can be found [here](https://github.com/fraillt/cpp_serializers_benchmark.git)*
|
||||
|
||||
If still not convinced read more in library [motivation](doc/design/README.md) section.
|
||||
|
||||
## 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 +1,2 @@
|
||||
errors handling design
|
||||
usage of NotDefinedType, and how it helps to reduce error stack
|
||||
@@ -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,4 +1,11 @@
|
||||
//include bitsery.h to get serialization and deserialization classes
|
||||
#include <bitsery/bitsery.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 };
|
||||
struct MyStruct {
|
||||
@@ -10,39 +17,34 @@ 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>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
|
||||
//create serializer
|
||||
//1) create buffer to store data
|
||||
std::vector<uint8_t> buffer;
|
||||
//2) create buffer writer that is able to write bytes or bits to buffer
|
||||
BufferWriter bw{buffer};
|
||||
//3) create serializer
|
||||
Serializer ser{bw};
|
||||
//create buffer to store data
|
||||
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);
|
||||
|
||||
//serialize object, can also be invoked like this: serialize(ser, data)
|
||||
ser.object(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);
|
||||
|
||||
//flush to buffer, before creating buffer reader
|
||||
bw.flush();
|
||||
|
||||
//create deserializer
|
||||
//1) create buffer reader
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
//2) create deserializer
|
||||
Deserializer des{br};
|
||||
|
||||
//deserialize same object, can also be invoked like this: serialize(des, data)
|
||||
des.object(res);
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
|
||||
103
examples/complex_flow.cpp
Normal file
103
examples/complex_flow.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <bitsery/bitsery.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 {
|
||||
|
||||
//define data
|
||||
enum Color:uint8_t { Red, Green, Blue };
|
||||
|
||||
struct Vec3 { float x, y, z; };
|
||||
|
||||
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 {
|
||||
Vec3 pos;
|
||||
int16_t mana;
|
||||
int16_t hp;
|
||||
std::string name;
|
||||
std::vector<uint8_t> inventory;
|
||||
Color color;
|
||||
std::vector<Weapon> weapons;
|
||||
Weapon equipped;
|
||||
std::vector<Vec3> path;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, Vec3& o) {
|
||||
s.value4b(o.x);
|
||||
s.value4b(o.y);
|
||||
s.value4b(o.z);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void serialize (S& s, Monster& o) {
|
||||
//forward/backward compatibility for monsters
|
||||
s.ext(o, bitsery::ext::Growable{}, [&s](Monster& o1) {
|
||||
s.value1b(o1.color);
|
||||
s.value2b(o1.mana);
|
||||
s.value2b(o1.hp);
|
||||
s.object(o1.equipped);
|
||||
s.object(o1.pos);
|
||||
s.container(o1.path, 1000);
|
||||
s.container(o1.weapons, 100);
|
||||
s.container1b(o1.inventory, 50);
|
||||
s.text1b(o1.name, 20);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//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;
|
||||
};
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
data.weapons.push_back(MyTypes::Weapon{"GoodWeapon", 100});
|
||||
|
||||
//create buffer to store data to
|
||||
Buffer buffer{};
|
||||
//since we're using different configuration, we cannot use quickSerialization function.
|
||||
BasicSerializer<AdapterWriter<OutputAdapter, SessionsEnabled>> ser{OutputAdapter{buffer}};
|
||||
ser.object(data);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
auto writtenSize = w.writtenBytesCount();
|
||||
|
||||
|
||||
MyTypes::Monster res{};
|
||||
//deserialize
|
||||
BasicDeserializer<AdapterReader<InputAdapter, SessionsEnabled>> des{InputAdapter{buffer.begin(), writtenSize}};
|
||||
des.object(res);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
assert(r.error() == ReaderError::NoError && r.isCompletedSuccessfully());
|
||||
}
|
||||
59
examples/file_stream.cpp
Normal file
59
examples/file_stream.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
//in order to work with streams include stream adapter
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
double f;
|
||||
};
|
||||
|
||||
//define how object should be serialized/deserialized
|
||||
template <typename S>
|
||||
void serialize(S& s, MyStruct& o) {
|
||||
s.value4b(o.i);
|
||||
s.value2b(o.e);
|
||||
s.value8b(o.f);
|
||||
};
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Stream = std::fstream;
|
||||
using IOAdapter = IOStreamAdapter;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, 0.045};
|
||||
MyStruct res{};
|
||||
|
||||
//open file stream for writing and reading
|
||||
auto fileName = "test_file.bin";
|
||||
Stream s{fileName, s.binary | s.trunc | s.out};
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for writing\n";
|
||||
return 0;
|
||||
}
|
||||
//use same quick serialization function
|
||||
//streams do not return written size
|
||||
quickSerialization<IOAdapter>(s, data);
|
||||
|
||||
s.close();
|
||||
//reopen for reading
|
||||
|
||||
s.open(fileName, s.binary | s.in);
|
||||
if (!s.is_open()) {
|
||||
std::cout << "cannot open " << fileName << " for reading\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
//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<IOAdapter>(s, res);
|
||||
|
||||
assert(state.first == ReaderError::NoError && state.second);
|
||||
assert(data.f == res.f && data.i == res.i && data.e == res.e);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/ext/growable.h>
|
||||
#include <array>
|
||||
|
||||
namespace MyTypes {
|
||||
|
||||
//define data
|
||||
enum Color:uint8_t { Red, Green, Blue };
|
||||
|
||||
struct Vec3 { float x, y, z; };
|
||||
|
||||
struct Weapon {
|
||||
std::string name;
|
||||
int16_t damage;
|
||||
};
|
||||
|
||||
struct Monster {
|
||||
Vec3 pos;
|
||||
int16_t mana;
|
||||
int16_t hp;
|
||||
std::string name;
|
||||
std::vector<uint8_t> inventory;
|
||||
Color color;
|
||||
std::vector<Weapon> weapons;
|
||||
Weapon equipped;
|
||||
std::vector<Vec3> path;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, Vec3& o) {
|
||||
s.value4b(o.x);
|
||||
s.value4b(o.y);
|
||||
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
|
||||
s.ext(o, bitsery::ext::Growable{}, [&s](Monster& o1) {
|
||||
s.value1b(o1.color);
|
||||
s.value2b(o1.mana);
|
||||
s.value2b(o1.hp);
|
||||
s.object(o1.equipped);
|
||||
s.object(o1.pos);
|
||||
s.container(o1.path, 1000);
|
||||
s.container(o1.weapons, 100);
|
||||
s.container1b(o1.inventory, 50);
|
||||
s.text1b(o1.name, 20);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//change configuration
|
||||
struct NonDefaultConfig: public bitsery::DefaultConfig {
|
||||
//change underlying buffer
|
||||
using BufferType = std::array<uint8_t, 1000000>;
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyTypes::Monster data{};
|
||||
data.name = "lew";
|
||||
|
||||
//create serializer
|
||||
//1) create buffer to store data
|
||||
std::array<uint8_t, 1000000> buffer{};
|
||||
//2) create buffer writer that is able to write bytes or bits to buffer
|
||||
BasicBufferWriter<NonDefaultConfig> bw{buffer};
|
||||
//3) create serializer
|
||||
BasicSerializer<NonDefaultConfig> ser{bw};
|
||||
|
||||
//serialize object, can also be invoked like this: serialize(ser, data)
|
||||
ser.object(data);
|
||||
|
||||
//flush to buffer, before creating buffer reader, this will always write sessions data for forward/backward compatibility
|
||||
bw.flush();
|
||||
|
||||
//create deserializer
|
||||
//1) create buffer reader
|
||||
BasicBufferReader<NonDefaultConfig> br{bw.getWrittenRange()};
|
||||
//2) create deserializer
|
||||
BasicDeserializer<NonDefaultConfig> des{br};
|
||||
|
||||
//deserialize same object, can also be invoked like this: serialize(des, data)
|
||||
MyTypes::Monster res{};
|
||||
des.object(res);
|
||||
|
||||
}
|
||||
45
examples/flexible_assert_linux_x64.cpp
Normal file
45
examples/flexible_assert_linux_x64.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/flexible.h>
|
||||
#include <bitsery/flexible/vector.h>
|
||||
|
||||
|
||||
struct MyStruct {
|
||||
int i;
|
||||
unsigned short s;
|
||||
std::vector<long> vl;
|
||||
long long ll;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
//now we can use flexible syntax with
|
||||
//member function has same name as parameter
|
||||
s.archive(this->s, i, vl, ll);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//this will only work on linux or mac x64
|
||||
bitsery::assertFundamentalTypeSizes<2,4,8,8>();
|
||||
//set some random data
|
||||
MyStruct data{8941, 3, {15l, -8l, 045l}, 8459845ll};
|
||||
MyStruct res{};
|
||||
|
||||
//serialization, deserialization flow is unchanged as in basic usage
|
||||
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.vl == res.vl && data.s == res.s && data.i == res.i && data.ll == res.ll);
|
||||
}
|
||||
47
examples/flexible_syntax.cpp
Normal file
47
examples/flexible_syntax.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
//include flexible header, to use flexible syntax
|
||||
#include <bitsery/flexible.h>
|
||||
//we also need additional traits to work with container types,
|
||||
//instead of including <bitsery/traits/vector.h> for vector traits, now we also need traits to work with flexible types.
|
||||
//so include everything from <bitsery/flexible/...> instead of <bitsery/traits/...>
|
||||
//otherwise we'll get static assert error, saying to define serialize function.
|
||||
#include <bitsery/flexible/vector.h>
|
||||
|
||||
enum class MyEnum:uint16_t { V1,V2,V3 };
|
||||
struct MyStruct {
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
|
||||
//define serialize function as usual
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
//now we can use flexible syntax with
|
||||
s.archive(i, e, fs);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
using namespace bitsery;
|
||||
|
||||
//some helper types
|
||||
using Buffer = std::vector<uint8_t>;
|
||||
using OutputAdapter = OutputBufferAdapter<Buffer>;
|
||||
using InputAdapter = InputBufferAdapter<Buffer>;
|
||||
|
||||
int main() {
|
||||
//set some random data
|
||||
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
|
||||
MyStruct res{};
|
||||
|
||||
//serialization, deserialization flow is unchanged as in basic usage
|
||||
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);
|
||||
}
|
||||
194
include/bitsery/adapter/buffer.h
Normal file
194
include/bitsery/adapter/buffer.h
Normal file
@@ -0,0 +1,194 @@
|
||||
//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_INPUT_BUFFER_ADAPTER_H
|
||||
#define BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_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 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;
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator end): BufferIterators<Buffer>(begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
:InputBufferAdapter(begin, std::next(begin, size))
|
||||
{
|
||||
}
|
||||
|
||||
void read(TValue* data, size_t size) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, this->endIt) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
} else {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
|
||||
if (error() == ReaderError::NoError)
|
||||
setError(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto res = std::distance(this->endIt, this->posIt);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<ReaderError>(res);
|
||||
return err;
|
||||
}
|
||||
return ReaderError::NoError;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
this->endIt = this->posIt;
|
||||
//to avoid creating temporary for error state, mark an error by passing posIt after the endIt
|
||||
std::advance(this->posIt, static_cast<size_t>(error));
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Container>
|
||||
class OutputBufferAdapter {
|
||||
public:
|
||||
|
||||
using TIterator = typename traits::BufferAdapterTraits<Container>::TIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<Container>::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
|
||||
OutputBufferAdapter(Container &buffer)
|
||||
: _buffer{buffer}
|
||||
{
|
||||
|
||||
init(TResizable{});
|
||||
}
|
||||
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
writeInternal(data, size, TResizable{});
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Container>::isResizable>;
|
||||
|
||||
Container &_buffer;
|
||||
TIterator _outIt;
|
||||
TIterator _end;
|
||||
|
||||
/*
|
||||
* resizable buffer
|
||||
*/
|
||||
|
||||
void init(std::true_type) {
|
||||
//resize buffer immediately, because we need output iterator at valid position
|
||||
if (traits::ContainerTraits<Container>::size(_buffer) == 0u) {
|
||||
traits::BufferAdapterTraits<Container>::increaseBufferSize(_buffer);
|
||||
}
|
||||
_end = std::end(_buffer);
|
||||
_outIt = std::begin(_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
} else {
|
||||
_outIt -= size;
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(_buffer), _outIt);
|
||||
//increase container size
|
||||
traits::BufferAdapterTraits<Container>::increaseBufferSize(_buffer);
|
||||
//restore iterators
|
||||
_end = std::end(_buffer);
|
||||
_outIt = std::next(std::begin(_buffer), pos);
|
||||
|
||||
writeInternal(data, size, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* non resizable buffer
|
||||
*/
|
||||
void init(std::false_type) {
|
||||
_outIt = std::begin(_buffer);
|
||||
_end = std::end(_buffer);
|
||||
}
|
||||
|
||||
void writeInternal(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_ADAPTERS_INPUT_BUFFER_ADAPTER_H
|
||||
122
include/bitsery/adapter/stream.h
Normal file
122
include/bitsery/adapter/stream.h
Normal file
@@ -0,0 +1,122 @@
|
||||
//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) {
|
||||
if (static_cast<size_t>(_ios.rdbuf()->sgetn( data , size )) != size)
|
||||
*data = {};
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
if (_ios.good())
|
||||
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
|
||||
269
include/bitsery/adapter_reader.h
Normal file
269
include/bitsery/adapter_reader.h
Normal file
@@ -0,0 +1,269 @@
|
||||
//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_BASIC_READER_H
|
||||
#define BITSERY_BASIC_READER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename TReader>
|
||||
class AdapterReaderBitPackingWrapper;
|
||||
|
||||
template<typename InputAdapter, typename Config>
|
||||
struct AdapterReader {
|
||||
//this is required by deserializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
using TValue = typename InputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
using TIterator = typename InputAdapter::TIterator;// used by session reader
|
||||
|
||||
explicit AdapterReader(InputAdapter&& adapter)
|
||||
: _inputAdapter{std::move(adapter)},
|
||||
_session{*this, _inputAdapter}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReader(const AdapterReader &) = delete;
|
||||
|
||||
AdapterReader &operator=(const AdapterReader &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterReader(AdapterReader &&) = default;
|
||||
|
||||
AdapterReader &operator=(AdapterReader &&) = default;
|
||||
|
||||
~AdapterReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(buf, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &, size_t ) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Deserializer with bit packing enabled.");
|
||||
}
|
||||
|
||||
void align() {
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _inputAdapter.isCompletedSuccessfully() && !_session.hasActiveSessions();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
auto err = _inputAdapter.error();
|
||||
if (err == ReaderError::DataOverflow && _session.hasActiveSessions())
|
||||
return ReaderError::NoError;
|
||||
return err;
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
return _inputAdapter.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
if (error() == ReaderError::NoError) {
|
||||
_session.end();
|
||||
}
|
||||
}
|
||||
|
||||
const InputAdapter& adapter() const {
|
||||
return _inputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterReaderBitPackingWrapper<AdapterReader<InputAdapter, Config>>;
|
||||
|
||||
InputAdapter _inputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsReader<AdapterReader<InputAdapter, Config>>,
|
||||
session::DisabledSessionsReader<AdapterReader<InputAdapter, Config>>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
_inputAdapter.read(reinterpret_cast<TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TReader>
|
||||
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");
|
||||
|
||||
explicit AdapterReaderBitPackingWrapper(TReader& reader):_reader{reader}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterReaderBitPackingWrapper(const AdapterReaderBitPackingWrapper&) = delete;
|
||||
AdapterReaderBitPackingWrapper& operator = (const AdapterReaderBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterReaderBitPackingWrapper(AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterReaderBitPackingWrapper& operator = (AdapterReaderBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterReaderBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
_reader.template readBytes<SIZE,T>(v);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
_reader.template readBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _reader.isCompletedSuccessfully();
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _reader.error();
|
||||
}
|
||||
|
||||
void setError(ReaderError error) {
|
||||
_reader.setError(error);
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_reader.beginSession();
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_reader.endSession();
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
|
||||
template<typename T>
|
||||
void readBitsInternal(T &v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BitsSize<UnsignedValue>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
UnsignedValue tmp;
|
||||
_reader.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BitsSize<UnsignedValue>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = res;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_READER_H
|
||||
337
include/bitsery/adapter_writer.h
Normal file
337
include/bitsery/adapter_writer.h
Normal file
@@ -0,0 +1,337 @@
|
||||
//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_BASIC_WRITER_H
|
||||
#define BITSERY_BASIC_WRITER_H
|
||||
|
||||
#include "details/sessions.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
|
||||
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::BitsSize<T>::value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(bitsCount <= details::BitsSize<T>::value);
|
||||
_bitsCount += bitsCount;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BitsSize<T>::value * count;
|
||||
}
|
||||
|
||||
void align() {
|
||||
auto _scratch = (_bitsCount % 8);
|
||||
_bitsCount += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
//flush sessions count
|
||||
if (_sessionsBytesCount > 0) {
|
||||
_bitsCount += (_sessionsBytesCount + 4) * 8;
|
||||
_sessionsBytesCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
auto endPos = writtenBytesCount();
|
||||
details::writeSize(*this, endPos);
|
||||
auto sessionEndBytesCount = writtenBytesCount() - endPos;
|
||||
//remove written bytes, because we'll write them at the end
|
||||
_bitsCount -= sessionEndBytesCount * 8;
|
||||
_sessionsBytesCount += sessionEndBytesCount;
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t writtenBytesCount() const {
|
||||
return _bitsCount / 8;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _bitsCount{};
|
||||
size_t _sessionsBytesCount{};
|
||||
};
|
||||
|
||||
|
||||
template <typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper;
|
||||
|
||||
template<typename OutputAdapter, typename Config>
|
||||
struct AdapterWriter {
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = false;
|
||||
|
||||
using TValue = typename OutputAdapter::TValue;
|
||||
|
||||
static_assert(details::IsDefined<TValue>::value, "Please define adapter traits or include from <bitsery/traits/...>");
|
||||
|
||||
explicit AdapterWriter(OutputAdapter&& adapter)
|
||||
: _outputAdapter{std::move(adapter)}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriter(const AdapterWriter &) = delete;
|
||||
|
||||
AdapterWriter &operator=(const AdapterWriter &) = delete;
|
||||
|
||||
//todo add conditional noexcept
|
||||
AdapterWriter(AdapterWriter &&) = default;
|
||||
|
||||
AdapterWriter &operator=(AdapterWriter &&) = default;
|
||||
|
||||
~AdapterWriter() {
|
||||
flush();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(&v, 1);
|
||||
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directWrite(buf, count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &, size_t ) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Serializer with bit packing enabled.");
|
||||
}
|
||||
|
||||
//to have the same interface as bitpackingwriter
|
||||
void align() {
|
||||
|
||||
}
|
||||
|
||||
void flush() {
|
||||
_session.flushSessions(*this);
|
||||
_outputAdapter.flush();
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _outputAdapter.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
_session.begin(*this);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
_session.end(*this);
|
||||
}
|
||||
|
||||
const OutputAdapter& adapter() const {
|
||||
return _outputAdapter;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AdapterWriterBitPackingWrapper<AdapterWriter<OutputAdapter, Config>>;
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
_outputAdapter.write(reinterpret_cast<const TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
OutputAdapter _outputAdapter;
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
session::SessionsWriter<AdapterWriter<OutputAdapter, Config >>,
|
||||
session::DisabledSessionsWriter<AdapterWriter<OutputAdapter, Config>>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
//this class is used as wrapper for real AdapterWriter, it doesn't store writer itself just a reference
|
||||
template<typename TWriter>
|
||||
class AdapterWriterBitPackingWrapper {
|
||||
public:
|
||||
//this is required by serializer
|
||||
static constexpr bool BitPackingEnabled = true;
|
||||
|
||||
//make TValue unsigned for bit packing
|
||||
using UnsignedType = typename std::make_unsigned<typename TWriter::TValue>::type;
|
||||
using ScratchType = typename details::ScratchType<UnsignedType>::type;
|
||||
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
|
||||
|
||||
explicit AdapterWriterBitPackingWrapper(TWriter &writer)
|
||||
: _writer{writer}
|
||||
{
|
||||
}
|
||||
|
||||
AdapterWriterBitPackingWrapper(const AdapterWriterBitPackingWrapper&) = delete;
|
||||
AdapterWriterBitPackingWrapper& operator = (const AdapterWriterBitPackingWrapper&) = delete;
|
||||
|
||||
AdapterWriterBitPackingWrapper(AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
AdapterWriterBitPackingWrapper& operator = (AdapterWriterBitPackingWrapper&& ) noexcept = default;
|
||||
|
||||
~AdapterWriterBitPackingWrapper() {
|
||||
align();
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBytes<SIZE,T>(v);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
_writer.template writeBuffer<SIZE,T>(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBits(const T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
|
||||
assert(v <= (bitsCount < 64
|
||||
? (1ULL << bitsCount) - 1
|
||||
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
_writer._session.flushSessions(_writer);
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
return _writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_writer._session.begin(_writer);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_writer._session.end(_writer);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value >>= valueSize;
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
//overload for TValue, for better performance
|
||||
void writeBitsInternal(const UnsignedType &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
|
||||
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
|
||||
_writer.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
|
||||
_scratch >>= details::BitsSize<UnsignedType>::value;
|
||||
_scratchBits -= details::BitsSize<UnsignedType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UnsignedType _MASK = std::numeric_limits<UnsignedType>::max();
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
TWriter& _writer;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
@@ -37,9 +37,6 @@ BITSERY_QUOTE_MACRO(patch)
|
||||
#define BITSERY_VERSION \
|
||||
BITSERY_BUILD_VERSION_STR(BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
|
||||
|
||||
|
||||
#include "buffer_writer.h"
|
||||
#include "buffer_reader.h"
|
||||
#include "serializer.h"
|
||||
#include "deserializer.h"
|
||||
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_BUFFER_READER_H
|
||||
#define BITSERY_BUFFER_READER_H
|
||||
|
||||
#include "details/buffer_common.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename Config>
|
||||
struct BasicBufferReader {
|
||||
|
||||
using BufferType = typename Config::BufferType;
|
||||
using ValueType = typename details::BufferContainerTraits<BufferType>::TValue;
|
||||
using BufferIteratorType = typename details::BufferContainerTraits<BufferType>::TIterator;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
|
||||
BasicBufferReader(ValueType* begin, ValueType* end)
|
||||
:_pos{begin},
|
||||
_end{end},
|
||||
_session{*this, _pos, _end}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BasicBufferReader(BufferRange<BufferIteratorType> range)
|
||||
:BasicBufferReader(std::addressof(*range.begin()), std::addressof(*range.end())) {
|
||||
static_assert(std::is_same<
|
||||
typename std::iterator_traits<BufferIteratorType>::iterator_category,
|
||||
std::random_access_iterator_tag>::value,
|
||||
"BufferReader only accepts random access iterators");
|
||||
}
|
||||
|
||||
BasicBufferReader(const BasicBufferReader &) = delete;
|
||||
|
||||
BasicBufferReader &operator=(const BasicBufferReader &) = delete;
|
||||
|
||||
BasicBufferReader(BasicBufferReader &&) noexcept = default;
|
||||
|
||||
BasicBufferReader &operator=(BasicBufferReader &&) noexcept = default;
|
||||
|
||||
~BasicBufferReader() noexcept = default;
|
||||
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBytes(T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
if (!m_scratchBits)
|
||||
directRead(&v, 1);
|
||||
else
|
||||
readBits(reinterpret_cast<UT &>(v), details::BITS_SIZE<T>::value);
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!m_scratchBits) {
|
||||
directRead(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
readBits(reinterpret_cast<UT &>(*it), details::BITS_SIZE<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void readBits(T &v, size_t bitsCount) {
|
||||
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
|
||||
readBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
if (m_scratchBits) {
|
||||
ScratchType tmp{};
|
||||
readBitsInternal(tmp, m_scratchBits);
|
||||
if (tmp)
|
||||
setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return _pos == _end && !_session.hasActiveSessions();
|
||||
}
|
||||
|
||||
BufferReaderError getError() const {
|
||||
auto res = std::distance(_end, _pos);
|
||||
if (res > 0) {
|
||||
auto err = static_cast<BufferReaderError>(res);
|
||||
if (_session.hasActiveSessions() && err == BufferReaderError::BUFFER_OVERFLOW)
|
||||
return BufferReaderError::NO_ERROR;
|
||||
return err;
|
||||
}
|
||||
return BufferReaderError::NO_ERROR;
|
||||
}
|
||||
|
||||
void setError(BufferReaderError error) {
|
||||
_end = _pos;
|
||||
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
|
||||
std::advance(_pos, static_cast<size_t>(error));
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
if (getError() != BufferReaderError::INVALID_BUFFER_DATA) {
|
||||
_session.begin();
|
||||
}
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
if (getError() != BufferReaderError::INVALID_BUFFER_DATA) {
|
||||
_session.end();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType* _pos;
|
||||
ValueType* _end;
|
||||
ScratchType m_scratch{};
|
||||
size_t m_scratchBits{};
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
details::BufferSessionsReader<BasicBufferReader<Config>, ValueType*>,
|
||||
details::DisabledBufferSessionsReader<Config>>::type
|
||||
_session;
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
const auto bytesCount = sizeof(T) * count;
|
||||
|
||||
if (std::distance(_pos, _end) >= static_cast<typename details::BufferContainerTraits<BufferType>::TDifference>(bytesCount)) {
|
||||
|
||||
std::memcpy(reinterpret_cast<ValueType *>(v), _pos, bytesCount);
|
||||
_pos += bytesCount;
|
||||
|
||||
//swap each byte if nessesarry
|
||||
_swapDataBits(v, count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
} else {
|
||||
//set everything to zeros
|
||||
std::memset(v, 0, bytesCount);
|
||||
|
||||
if (getError() == BufferReaderError::NO_ERROR)
|
||||
setError(BufferReaderError::BUFFER_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readBitsInternal(T &v, size_t size) {
|
||||
auto bitsLeft = size;
|
||||
T res{};
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, details::BITS_SIZE<ValueType>::value);
|
||||
if (m_scratchBits < bits) {
|
||||
ValueType tmp;
|
||||
directRead(&tmp, 1);
|
||||
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
|
||||
m_scratchBits += details::BITS_SIZE<ValueType>::value;
|
||||
}
|
||||
auto shiftedRes =
|
||||
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
|
||||
res |= shiftedRes;
|
||||
m_scratch >>= bits;
|
||||
m_scratchBits -= bits;
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
v = res;
|
||||
}
|
||||
|
||||
};
|
||||
//helper type
|
||||
using BufferReader = BasicBufferReader<DefaultConfig>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_READER_H
|
||||
@@ -1,253 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
|
||||
#ifndef BITSERY_BUFFER_WRITER_H
|
||||
#define BITSERY_BUFFER_WRITER_H
|
||||
|
||||
#include "details/buffer_common.h"
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
struct MeasureSize {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
_bitsCount += bitsCount;
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
_bitsCount += details::BITS_SIZE<T>::value * count;
|
||||
}
|
||||
|
||||
void align() {
|
||||
auto _scratch = (_bitsCount % 8);
|
||||
_bitsCount += (8 - _scratch) % 8;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
//flush sessions count
|
||||
if (_sessionsBytesCount > 0) {
|
||||
auto sessionsDataSizeBytesCount = (_sessionsBytesCount < 0x8000u ? 2 : 4);
|
||||
_bitsCount += (_sessionsBytesCount + sessionsDataSizeBytesCount) * 8;
|
||||
_sessionsBytesCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
auto endPos = getWrittenBytesCount();
|
||||
details::writeSize(*this, endPos);
|
||||
auto sessionEndBytesCount = getWrittenBytesCount() - endPos;
|
||||
//remove written bytes, because we'll write them at the end
|
||||
_bitsCount -= sessionEndBytesCount * 8;
|
||||
_sessionsBytesCount += sessionEndBytesCount;
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t getWrittenBytesCount() const {
|
||||
return _bitsCount / 8;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _bitsCount{};
|
||||
size_t _sessionsBytesCount{};
|
||||
};
|
||||
|
||||
template<typename Config>
|
||||
struct BasicBufferWriter {
|
||||
using BufferType = typename Config::BufferType;
|
||||
using ValueType = typename details::BufferContainerTraits<BufferType>::TValue;
|
||||
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
|
||||
using BufferContext = details::WriteBufferContext<BufferType, details::BufferContainerTraits<BufferType>::isResizable>;
|
||||
|
||||
explicit BasicBufferWriter(BufferType &buffer)
|
||||
: _bufferContext{buffer}
|
||||
{
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferType value type must be unsigned");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BasicBufferWriter(const BasicBufferWriter &) = delete;
|
||||
|
||||
BasicBufferWriter &operator=(const BasicBufferWriter &) = delete;
|
||||
|
||||
BasicBufferWriter(BasicBufferWriter &&) noexcept = default;
|
||||
|
||||
BasicBufferWriter &operator=(BasicBufferWriter &&) noexcept = default;
|
||||
|
||||
~BasicBufferWriter() noexcept = default;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
|
||||
if (!_scratchBits) {
|
||||
directWrite(&v, 1);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
writeBits(reinterpret_cast<const UT &>(v), details::BITS_SIZE<T>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
if (!_scratchBits) {
|
||||
directWrite(buf, count);
|
||||
} else {
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
//todo improve implementation
|
||||
const auto end = buf + count;
|
||||
for (auto it = buf; it != end; ++it)
|
||||
writeBits(reinterpret_cast<const UT &>(*it), details::BITS_SIZE<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(v <= (bitsCount < 64
|
||||
? (1ULL << bitsCount) - 1
|
||||
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
|
||||
writeBitsInternal(v, bitsCount);
|
||||
}
|
||||
|
||||
void align() {
|
||||
writeBitsInternal(ValueType{}, (details::BITS_SIZE<ValueType>::value - _scratchBits) % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
align();
|
||||
_session.flushSessions(*this);
|
||||
}
|
||||
|
||||
BufferRange<typename details::BufferContainerTraits<BufferType>::TIterator> getWrittenRange() const {
|
||||
return _bufferContext.getWrittenRange();
|
||||
}
|
||||
|
||||
void beginSession() {
|
||||
align();
|
||||
_session.begin(*this);
|
||||
}
|
||||
|
||||
void endSession() {
|
||||
align();
|
||||
_session.end(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
_bufferContext.write(reinterpret_cast<const ValueType *>(&res), sizeof(T));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
_bufferContext.write(reinterpret_cast<const ValueType *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeBitsInternal(const T &v, size_t size) {
|
||||
constexpr size_t valueSize = details::BITS_SIZE<ValueType>::value;
|
||||
auto value = v;
|
||||
auto bitsLeft = size;
|
||||
while (bitsLeft > 0) {
|
||||
auto bits = std::min(bitsLeft, valueSize);
|
||||
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
|
||||
_scratchBits += bits;
|
||||
if (_scratchBits >= valueSize) {
|
||||
auto tmp = static_cast<ValueType>(_scratch & _MASK);
|
||||
directWrite(&tmp, 1);
|
||||
_scratch >>= valueSize;
|
||||
_scratchBits -= valueSize;
|
||||
|
||||
value >>= valueSize;
|
||||
}
|
||||
bitsLeft -= bits;
|
||||
}
|
||||
}
|
||||
|
||||
//overload for ValueType, for better performance
|
||||
void writeBitsInternal(const ValueType &v, size_t size) {
|
||||
if (size > 0) {
|
||||
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
|
||||
_scratchBits += size;
|
||||
if (_scratchBits >= details::BITS_SIZE<ValueType>::value) {
|
||||
auto tmp = static_cast<ValueType>(_scratch & _MASK);
|
||||
directWrite(&tmp, 1);
|
||||
_scratch >>= details::BITS_SIZE<ValueType>::value;
|
||||
_scratchBits -= details::BITS_SIZE<ValueType>::value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ValueType _MASK = std::numeric_limits<ValueType>::max();
|
||||
BufferContext _bufferContext;
|
||||
ScratchType _scratch{};
|
||||
size_t _scratchBits{};
|
||||
typename std::conditional<Config::BufferSessionsEnabled,
|
||||
details::BufferSessionsWriter<BasicBufferWriter<Config>>,
|
||||
details::DisabledBufferSessionsWriter<Config>>::type
|
||||
_session{};
|
||||
};
|
||||
|
||||
//helper type
|
||||
using BufferWriter = BasicBufferWriter<DefaultConfig>;
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_WRITER_H
|
||||
@@ -24,8 +24,6 @@
|
||||
#ifndef BITSERY_COMMON_H
|
||||
#define BITSERY_COMMON_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
/*
|
||||
@@ -38,13 +36,11 @@ 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 for any type
|
||||
//disabling it, saves 100+bytes per BufferReader/Writer and also reduces executable size
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
//buffer value type must be unsigned, currently only uint8_t supported
|
||||
//fixed size buffer type also supported, for faster serialization performance
|
||||
using BufferType = std::vector<uint8_t>;
|
||||
//this functionality allows to support backward/forward compatibility
|
||||
//however reading from streams is not supported, because this functionality requires random access to buffer.
|
||||
static constexpr bool BufferSessionsEnabled = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -24,23 +24,43 @@
|
||||
#ifndef BITSERY_DESERIALIZER_H
|
||||
#define BITSERY_DESERIALIZER_H
|
||||
|
||||
#include "common.h"
|
||||
#include "details/serialization_common.h"
|
||||
#include "adapter_reader.h"
|
||||
#include <utility>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename Config>
|
||||
template<typename TAdapterReader>
|
||||
class BasicDeserializer {
|
||||
public:
|
||||
explicit BasicDeserializer(BasicBufferReader<Config> &r, void* context = nullptr) : _reader{r}, _context{context} {};
|
||||
//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>;
|
||||
|
||||
|
||||
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&& ) = 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;
|
||||
}
|
||||
|
||||
@@ -59,24 +79,32 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
/*
|
||||
* value overloads
|
||||
* functionality, that enables simpler serialization syntax, by including additional header
|
||||
*/
|
||||
template<typename T, typename ... TArgs>
|
||||
void archive(T &&head, TArgs &&... tail) {
|
||||
//serialize object
|
||||
details::ArchiveFunction<BasicDeserializer, T>::invoke(*this, std::forward<T>(head));
|
||||
//expand other elements
|
||||
archive(std::forward<TArgs>(tail)...);
|
||||
}
|
||||
|
||||
/*
|
||||
* value
|
||||
*/
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
|
||||
void value(T &v) {
|
||||
static_assert(std::numeric_limits<T>::is_iec559, "");
|
||||
_reader.template readBytes<VSIZE>(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v));
|
||||
using TValue = typename details::IntegralFromFundamental<T>::TValue;
|
||||
_reader.template readBytes<VSIZE>(reinterpret_cast<TValue &>(v));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
void value(T &v) {
|
||||
using UT = typename std::underlying_type<T>::type;
|
||||
_reader.template readBytes<VSIZE>(reinterpret_cast<UT &>(v));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
void value(T &v) {
|
||||
_reader.template readBytes<VSIZE>(v);
|
||||
/*
|
||||
* enable bit-packing
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -84,49 +112,38 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
void ext(T &obj, Ext &&extension, Fnc &&fnc) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportLambdaOverload,
|
||||
void ext(T &obj, const Ext &extension, Fnc &&fnc) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
extension.deserialize(*this, _reader, obj, std::forward<Fnc>(fnc));
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
void ext(T &obj, Ext &&extension) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportValueOverload,
|
||||
void ext(T &obj, const Ext &extension) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.deserialize(*this, _reader, obj, [this](VType &v) { value<VSIZE>(v); });
|
||||
extension.deserialize(*this, _reader, obj, [this](VType &v) { value<VSIZE>(v);});
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
void ext(T &obj, Ext &&extension) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportObjectOverload,
|
||||
void ext(T &obj, const Ext &extension) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.deserialize(*this, _reader, obj, [this](VType &v) { object(v); });
|
||||
};
|
||||
|
||||
/*
|
||||
* bool
|
||||
* boolValue
|
||||
*/
|
||||
|
||||
void boolBit(bool &v) {
|
||||
uint8_t tmp{};
|
||||
_reader.readBits(tmp, 1);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
void boolByte(bool &v) {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
v = tmp == 1;
|
||||
void boolValue(bool &v) {
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterReader::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -135,42 +152,25 @@ namespace bitsery {
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(T &str, size_t maxSize) {
|
||||
static_assert(details::TextTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&) overload without `maxSize` for static containers");
|
||||
size_t size;
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::TextTraits<T>::resize(str, size);
|
||||
auto begin = std::begin(str);
|
||||
auto end = std::next(begin, size);
|
||||
procContainer<VSIZE>(begin, end, std::true_type{});
|
||||
//null terminated character at the end
|
||||
*end = {};
|
||||
size_t length;
|
||||
details::readSize(_reader, length, maxSize);
|
||||
traits::ContainerTraits<T>::resize(str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(T &str) {
|
||||
static_assert(!details::TextTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
size_t size;
|
||||
auto begin = std::begin(str);
|
||||
auto containerEnd = std::end(str);
|
||||
assert(begin != containerEnd);
|
||||
details::readSize(_reader, size, static_cast<size_t>(std::distance(begin, containerEnd) - 1));
|
||||
//end of string, not en
|
||||
auto end = std::next(begin, size);
|
||||
procContainer<VSIZE>(std::begin(str), std::end(str), std::true_type{});
|
||||
//null terminated character at the end
|
||||
*end = {};
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
void text(T (&str)[N]) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, N - 1);
|
||||
auto first = std::begin(str);
|
||||
procContainer<VSIZE>(first, std::next(first, size), std::true_type{});
|
||||
//null-terminated string
|
||||
str[size] = {};
|
||||
size_t length;
|
||||
details::readSize(_reader, length, traits::ContainerTraits<T>::size(str));
|
||||
procText<VSIZE>(str, length);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -181,75 +181,69 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void container(T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&) overload without `maxSize` for static containers");
|
||||
size_t size{};
|
||||
details::readSize(_reader, size, maxSize);
|
||||
details::ContainerTraits<T>::resize(obj, size);
|
||||
traits::ContainerTraits<T>::resize(obj, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
//fixed size containers
|
||||
|
||||
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
|
||||
void container(T &obj, Fnc &&fnc) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
void container(T (&arr)[N], Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
void container(T (&arr)[N]) {
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container(T (&arr)[N]) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
}
|
||||
|
||||
void align() {
|
||||
_reader.align();
|
||||
}
|
||||
@@ -289,14 +283,14 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text1b(T (&str)[N]) { text<1>(str); }
|
||||
template<typename T>
|
||||
void text1b(T &str) { text<1>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text2b(T (&str)[N]) { text<2>(str); }
|
||||
template<typename T>
|
||||
void text2b(T &str) { text<2>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text4b(T (&str)[N]) { text<4>(str); }
|
||||
template<typename T>
|
||||
void text4b(T &str) { text<4>(str); }
|
||||
|
||||
template<typename T>
|
||||
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
|
||||
@@ -322,21 +316,10 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container1b(T (&arr)[N]) { container<1>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container2b(T (&arr)[N]) { container<2>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container4b(T (&arr)[N]) { container<4>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container8b(T (&arr)[N]) { container<8>(arr); }
|
||||
|
||||
private:
|
||||
BasicBufferReader<Config> &_reader;
|
||||
friend AdapterAccess;
|
||||
|
||||
TAdapterReader _reader;
|
||||
void* _context;
|
||||
|
||||
//process value types
|
||||
@@ -351,8 +334,10 @@ namespace bitsery {
|
||||
//true_type means, that we can copy whole buffer
|
||||
template<size_t VSIZE, typename It>
|
||||
void procContainer(It first, It last, std::true_type) {
|
||||
using TValue = typename std::decay<decltype(*first)>::type;
|
||||
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
|
||||
if (first != last)
|
||||
_reader.template readBuffer<VSIZE>(&(*first), std::distance(first, last));
|
||||
_reader.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), std::distance(first, last));
|
||||
};
|
||||
|
||||
//process by calling functions
|
||||
@@ -369,6 +354,46 @@ namespace bitsery {
|
||||
object(*first);
|
||||
};
|
||||
|
||||
template <size_t VSIZE, typename T>
|
||||
void procText(T& str, size_t length) {
|
||||
auto begin = std::begin(str);
|
||||
//end of string, not end of container
|
||||
auto end = std::next(begin, length);
|
||||
procContainer<VSIZE>(begin, end, std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
//null terminated character at the end
|
||||
if (traits::TextTraits<T>::addNUL)
|
||||
*end = {};
|
||||
}
|
||||
|
||||
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
|
||||
void procBoolValue(bool &v, std::true_type) {
|
||||
uint8_t tmp{};
|
||||
_reader.readBits(tmp, 1);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
void procBoolValue(bool &v, std::false_type) {
|
||||
unsigned char tmp;
|
||||
_reader.template readBytes<1>(tmp);
|
||||
if (tmp > 1)
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
v = tmp == 1;
|
||||
}
|
||||
|
||||
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
|
||||
fnc(*this);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
BasicDeserializer<AdapterReaderBitPackingWrapper<TAdapterReader>> tmp(_reader, _context);
|
||||
fnc(tmp);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
void object(details::DummyType&) {
|
||||
|
||||
@@ -379,10 +404,24 @@ namespace bitsery {
|
||||
|
||||
}
|
||||
|
||||
//dummy function, that stops archive variadic arguments expansion
|
||||
void archive() {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//helper type
|
||||
using Deserializer = BasicDeserializer<DefaultConfig>;
|
||||
template <typename Adapter>
|
||||
using Deserializer = BasicDeserializer<AdapterReader<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after deserialziation returns status
|
||||
template <typename Adapter, typename T>
|
||||
std::pair<ReaderError, bool> quickDeserialization(Adapter adapter, T& value) {
|
||||
Deserializer<Adapter> des{std::move(adapter)};
|
||||
des.object(value);
|
||||
auto& r = AdapterAccess::getReader(des);
|
||||
return {r.error(), r.isCompletedSuccessfully()};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
127
include/bitsery/details/adapter_common.h
Normal file
127
include/bitsery/details/adapter_common.h
Normal file
@@ -0,0 +1,127 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
#define BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include "adapter_utils.h"
|
||||
#include "not_defined_type.h"
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
|
||||
|
||||
};
|
||||
|
||||
//add swap functions to class, to avoid compilation warning about unused functions
|
||||
struct SwapImpl {
|
||||
static uint64_t exec(uint64_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(value);
|
||||
#else
|
||||
value = ( value & 0x00000000FFFFFFFF ) << 32 | ( value & 0xFFFFFFFF00000000 ) >> 32;
|
||||
value = ( value & 0x0000FFFF0000FFFF ) << 16 | ( value & 0xFFFF0000FFFF0000 ) >> 16;
|
||||
value = ( value & 0x00FF00FF00FF00FF ) << 8 | ( value & 0xFF00FF00FF00FF00 ) >> 8;
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t exec(uint32_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32(value);
|
||||
#else
|
||||
return ( value & 0x000000ff ) << 24 | ( value & 0x0000ff00 ) << 8 | ( value & 0x00ff0000 ) >> 8 | ( value & 0xff000000 ) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint16_t exec(uint16_t value) {
|
||||
return (value & 0x00ff) << 8 | (value & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
static uint8_t exec(uint8_t value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TValue>
|
||||
TValue swap(TValue value) {
|
||||
constexpr size_t TSize = sizeof(TValue);
|
||||
using UT = typename std::conditional<TSize == 1, uint8_t,
|
||||
typename std::conditional<TSize == 2, uint16_t,
|
||||
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::type;
|
||||
return SwapImpl::exec(static_cast<UT>(value));
|
||||
}
|
||||
|
||||
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
|
||||
struct EndiannessTestData {
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t &) _sample4Bytes;
|
||||
};
|
||||
|
||||
constexpr EndiannessType getSystemEndianness() {
|
||||
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01,
|
||||
"system must be either little or big endian");
|
||||
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct ScratchType {
|
||||
using type = NotDefinedType;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ScratchType<uint8_t> {
|
||||
using type = uint16_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* class used by session reader, to access underlying iterators of buffer
|
||||
*/
|
||||
struct SessionAccess {
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& posIteratorRef(TReader& r) {
|
||||
return r.posIt;
|
||||
}
|
||||
template <typename TReader, typename Iterator>
|
||||
static Iterator& endIteratorRef(TReader& r) {
|
||||
return r.endIt;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
@@ -23,15 +23,17 @@
|
||||
#ifndef BITSERY_DETAILS_BOTH_COMMON_H
|
||||
#define BITSERY_DETAILS_BOTH_COMMON_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
enum class BufferReaderError {
|
||||
NO_ERROR,
|
||||
BUFFER_OVERFLOW,
|
||||
INVALID_BUFFER_DATA
|
||||
enum class ReaderError {
|
||||
NoError,
|
||||
ReadingError, // this might be used with stream adapter
|
||||
DataOverflow,
|
||||
InvalidData
|
||||
};
|
||||
|
||||
namespace details {
|
||||
@@ -56,7 +58,7 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
if (size > maxSize) {
|
||||
r.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
r.setError(ReaderError::InvalidData);
|
||||
size = {};
|
||||
}
|
||||
}
|
||||
@@ -1,424 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
#define BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include "both_common.h"
|
||||
#include "traits.h"
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template<typename I>
|
||||
struct BufferRange : std::pair<I, I> {
|
||||
using std::pair<I, I>::pair;
|
||||
I begin() const { return this->first; }
|
||||
I end() const { return this->second; }
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
struct BITS_SIZE:public std::integral_constant<size_t, sizeof(T) << 3> {
|
||||
|
||||
};
|
||||
|
||||
//add swap functions to class, to avoid compilation warning about unused functions
|
||||
struct SwapImpl {
|
||||
static uint64_t exec(uint64_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(value);
|
||||
#else
|
||||
value = ( value & 0x00000000FFFFFFFF ) << 32 | ( value & 0xFFFFFFFF00000000 ) >> 32;
|
||||
value = ( value & 0x0000FFFF0000FFFF ) << 16 | ( value & 0xFFFF0000FFFF0000 ) >> 16;
|
||||
value = ( value & 0x00FF00FF00FF00FF ) << 8 | ( value & 0xFF00FF00FF00FF00 ) >> 8;
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t exec(uint32_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32(value);
|
||||
#else
|
||||
return ( value & 0x000000ff ) << 24 | ( value & 0x0000ff00 ) << 8 | ( value & 0x00ff0000 ) >> 8 | ( value & 0xff000000 ) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint16_t exec(uint16_t value) {
|
||||
return (value & 0x00ff) << 8 | (value & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
static uint8_t exec(uint8_t value) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TValue>
|
||||
TValue swap(TValue value) {
|
||||
constexpr size_t TSize = sizeof(TValue);
|
||||
using UT = typename std::conditional<TSize == 1, uint8_t,
|
||||
typename std::conditional<TSize == 2, uint16_t,
|
||||
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::type;
|
||||
return SwapImpl::exec(static_cast<UT>(value));
|
||||
}
|
||||
|
||||
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
|
||||
struct EndiannessTestData {
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t &) _sample4Bytes;
|
||||
};
|
||||
|
||||
constexpr EndiannessType getSystemEndianness() {
|
||||
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01,
|
||||
"system must be either little or big endian");
|
||||
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct SCRATCH_TYPE {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SCRATCH_TYPE<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;
|
||||
};
|
||||
|
||||
template <typename Config>
|
||||
struct DisabledBufferSessionsWriter {
|
||||
template <typename TWriter>
|
||||
void begin(TWriter& ) {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
template <typename TWriter>
|
||||
void end(TWriter& ) {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
template <typename TWriter>
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Config>
|
||||
struct DisabledBufferSessionsReader {
|
||||
template <typename TReader, typename TIterator>
|
||||
DisabledBufferSessionsReader(TReader& , TIterator& , TIterator& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(Config::BufferSessionsEnabled, "Buffer sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TWriter>
|
||||
class BufferSessionsWriter {
|
||||
public:
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
auto range = writer.getWrittenRange();
|
||||
*sessionIt = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto range = writer.getWrittenRange();
|
||||
auto dataSize = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
range = writer.getWrittenRange();
|
||||
auto totalSize = static_cast<size_t>(std::distance(range.begin(), range.end()));
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 2;//2 bytes for offset data
|
||||
if (sessionsOffset < 0x8000u) {
|
||||
writer.template writeBytes<2>(static_cast<uint16_t>(sessionsOffset));
|
||||
} else {
|
||||
//size doesnt fit in 2 bytes, write 4 bytes instead
|
||||
sessionsOffset+=2;
|
||||
uint16_t low = static_cast<uint16_t>(sessionsOffset);
|
||||
//mark most significant bit, that size is 4 bytes
|
||||
uint16_t high = static_cast<uint16_t>(0x8000u | (sessionsOffset >> 16));
|
||||
writer.template writeBytes<2>(low);
|
||||
writer.template writeBytes<2>(high);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex;
|
||||
};
|
||||
|
||||
template <typename TReader, typename TIterator>
|
||||
struct BufferSessionsReader {
|
||||
BufferSessionsReader(TReader& r, TIterator& begin, TIterator& end)
|
||||
:_reader{r},
|
||||
_begin{begin},
|
||||
_pos{begin},
|
||||
_end{end}
|
||||
{
|
||||
}
|
||||
void begin() {
|
||||
if (_sessions.empty())
|
||||
initializeSessions();
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_end);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_pos, _end) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_begin, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _end) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return;
|
||||
}
|
||||
_end = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_pos == _end || _reader.getError() == BufferReaderError::NO_ERROR)) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//getError() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_pos, _end);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_begin, _end));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
_pos = _end;
|
||||
//restore end position
|
||||
_end = _sessionsStack.top();
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _begin;
|
||||
TIterator& _pos;
|
||||
TIterator& _end;
|
||||
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
void initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _pos;
|
||||
//read size
|
||||
if (std::distance(_pos, _end) < 2) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_end, -2);
|
||||
_pos = endSessionsSizesIt;
|
||||
size_t sessionsOffset{};
|
||||
uint16_t high;
|
||||
_reader.template readBytes<2>(high);
|
||||
|
||||
|
||||
if (high >= 0x8000u) {
|
||||
endSessionsSizesIt = std::next(endSessionsSizesIt, -2);
|
||||
_pos = endSessionsSizesIt;
|
||||
if (std::distance(_begin, _pos) < 0) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return;
|
||||
}
|
||||
uint16_t low;
|
||||
_reader.template readBytes<2>(low);
|
||||
//mask out last bit
|
||||
high &= 0x7FFFu;
|
||||
sessionsOffset = static_cast<size_t>((high << 16) | low);
|
||||
|
||||
} else
|
||||
sessionsOffset = high;
|
||||
|
||||
auto bufferSize = std::distance(_begin, _end);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
return;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_pos = std::next(_end, -sessionsOffset);
|
||||
while (std::distance(_pos, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_pos = currPos;
|
||||
_end = std::next(_end, -sessionsOffset);
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
}
|
||||
};
|
||||
|
||||
//this class writes bytes and bits to underlying buffer, it has specializations for resizable and non-resizable buffers
|
||||
template<typename Buffer, bool isResizable>
|
||||
class WriteBufferContext {
|
||||
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class WriteBufferContext<Buffer, false> {
|
||||
public:
|
||||
using TValue = typename BufferContainerTraits<Buffer>::TValue;
|
||||
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
|
||||
using TDifference = typename BufferContainerTraits<Buffer>::TDifference;
|
||||
|
||||
|
||||
explicit WriteBufferContext(Buffer &buffer)
|
||||
: _buffer{buffer},
|
||||
_outIt{std::addressof(*std::begin(buffer))},
|
||||
_end{std::addressof(*std::end(buffer))}
|
||||
{
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
assert(std::distance(_outIt, _end) >= static_cast<TDifference>(size));
|
||||
memcpy(_outIt, data, size);
|
||||
_outIt += size;
|
||||
}
|
||||
|
||||
BufferRange<TIterator> getWrittenRange() const {
|
||||
auto begin = std::begin(_buffer);
|
||||
return BufferRange<TIterator>{begin, std::next(begin, _outIt - std::addressof(*begin))};
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer &_buffer;
|
||||
TValue* _outIt;
|
||||
TValue* _end;
|
||||
};
|
||||
|
||||
template<typename Buffer>
|
||||
class WriteBufferContext<Buffer, true> {
|
||||
public:
|
||||
using TValue = typename BufferContainerTraits<Buffer>::TValue;
|
||||
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
|
||||
using TDifference = typename BufferContainerTraits<Buffer>::TDifference;
|
||||
|
||||
explicit WriteBufferContext(Buffer &buffer)
|
||||
: _buffer{buffer}
|
||||
{
|
||||
getIterators(0);
|
||||
}
|
||||
|
||||
void write(const TValue *data, size_t size) {
|
||||
if ((_end - _outIt) >= static_cast< TDifference >(size)) {
|
||||
std::memcpy(_outIt, data, size);
|
||||
_outIt += size;
|
||||
} else {
|
||||
//get current position before invalidating iterators
|
||||
auto pos = std::distance(std::addressof(*std::begin(_buffer)), _outIt);
|
||||
//increase container size
|
||||
BufferContainerTraits<Buffer>::increaseBufferSize(_buffer);
|
||||
//restore iterators
|
||||
getIterators(pos);
|
||||
write(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
BufferRange<TIterator> getWrittenRange() const {
|
||||
auto begin = std::begin(_buffer);
|
||||
return BufferRange<TIterator>{begin, std::next(begin, _outIt - std::addressof(*begin))};
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void getIterators(TDifference writePos) {
|
||||
_end = std::addressof(*std::end(_buffer));
|
||||
_outIt = std::addressof(*std::next(std::begin(_buffer), writePos));
|
||||
}
|
||||
Buffer &_buffer;
|
||||
TValue* _outIt;
|
||||
TValue* _end;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_DETAILS_BUFFER_COMMON_H
|
||||
149
include/bitsery/details/flexible_common.h
Normal file
149
include/bitsery/details/flexible_common.h
Normal file
@@ -0,0 +1,149 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
#define BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
|
||||
#include "../traits/core/traits.h"
|
||||
#include <limits>
|
||||
|
||||
namespace bitsery {
|
||||
namespace flexible {
|
||||
|
||||
//these function overloads is required to apply maxSize, and optimize for fundamental types
|
||||
//for contigous arrays of fundamenal types, memcpy will be applied
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template container<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.container(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& !traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c) {
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template container<sizeof(TValue)>(c);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
|
||||
&& !traits::ContainerTraits<T>::isResizable
|
||||
>::type * = nullptr>
|
||||
void processContainer(S &s, T &c) {
|
||||
s.container(c);
|
||||
}
|
||||
|
||||
//overloads for text processing to apply maxSize
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
traits::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
void processText(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c, maxSize);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<
|
||||
!traits::ContainerTraits<T>::isResizable>::type * = nullptr>
|
||||
void processText(S &s, T &c) {
|
||||
using TValue = typename traits::ContainerTraits<T>::TValue;
|
||||
s.template text<sizeof(TValue)>(c);
|
||||
}
|
||||
|
||||
|
||||
//all wrapper functions, that modify behaviour, should inherit from this
|
||||
struct ArchiveWrapperFnc {
|
||||
|
||||
};
|
||||
|
||||
//this type is used to differentiate between container and text behaviour
|
||||
template<typename T, size_t N, bool isText>
|
||||
struct CArray : public ArchiveWrapperFnc {
|
||||
CArray(T (&data_)[N]) : data{data_} {};
|
||||
T (&data)[N];
|
||||
};
|
||||
|
||||
template<typename S, typename T, size_t N>
|
||||
void serialize(S &s, CArray<T, N, true> &str) {
|
||||
processText(s, str.data);
|
||||
}
|
||||
|
||||
template<typename S, typename T, size_t N>
|
||||
void serialize(S &s, CArray<T, N, false> &obj) {
|
||||
processContainer(s, obj.data);
|
||||
}
|
||||
|
||||
//used to set max container size
|
||||
template<typename T>
|
||||
struct MaxSize : public ArchiveWrapperFnc {
|
||||
MaxSize(T &data_, size_t maxSize_) : data{data_}, maxSize{maxSize_} {};
|
||||
T &data;
|
||||
size_t maxSize;
|
||||
};
|
||||
|
||||
//if container, then call procesContainer, this memcpy for fundamental types contiguous container
|
||||
template<typename S, typename T>
|
||||
void processMaxSize(S &s, T& data, size_t maxSize, std::true_type) {
|
||||
processContainer(s, data, maxSize);
|
||||
};
|
||||
|
||||
//overload for const T&
|
||||
template<typename S, typename T>
|
||||
void processMaxSize(S &s, const T& data, size_t maxSize, std::true_type) {
|
||||
processContainer(s, const_cast<T&>(data), maxSize);
|
||||
};
|
||||
|
||||
|
||||
//try to call serialize overload with maxsize, extensions use this technique
|
||||
template<typename S, typename T>
|
||||
void processMaxSize(S &s, T& data, size_t maxSize, std::false_type) {
|
||||
serialize(s, data, maxSize);
|
||||
};
|
||||
|
||||
//overload for const T&
|
||||
template<typename S, typename T>
|
||||
void processMaxSize(S &s, const T& data, size_t maxSize, std::false_type) {
|
||||
serialize(s, const_cast<T&>(data), maxSize);
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
void serialize(S &s, const MaxSize<T> &ms) {
|
||||
processMaxSize(s, ms.data, ms.maxSize, details::IsContainerTraitsDefined<typename std::decay<T>::type>{});
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_FLEXIBLE_COMMON_H
|
||||
78
include/bitsery/details/not_defined_type.h
Normal file
78
include/bitsery/details/not_defined_type.h
Normal file
@@ -0,0 +1,78 @@
|
||||
//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_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
#define BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
//this type is used to show clearer error messages
|
||||
struct NotDefinedType {
|
||||
//just swallow anything that is passed during creating
|
||||
template <typename ... T>
|
||||
NotDefinedType(T&& ...){};
|
||||
NotDefinedType() = default;
|
||||
//define operators so that we also swallow deeper errors, to reduce error stack
|
||||
//this time will be used as iterator, so define all operators nessesarry to work with iterators
|
||||
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
|
||||
return true;
|
||||
}
|
||||
friend bool operator != (const NotDefinedType&, const NotDefinedType&) {
|
||||
return false;
|
||||
}
|
||||
NotDefinedType& operator += (int) {
|
||||
return *this;
|
||||
}
|
||||
NotDefinedType& operator -= (int) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend int operator - (const NotDefinedType&, const NotDefinedType&) {
|
||||
return 0;
|
||||
}
|
||||
int& operator*() {
|
||||
return data;
|
||||
}
|
||||
int data;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsDefined:public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
//define iterator traits to work with standart algorithms
|
||||
template <>
|
||||
struct iterator_traits<bitsery::details::NotDefinedType> {
|
||||
using difference_type = int;
|
||||
using value_type = int;
|
||||
using pointer = int*;
|
||||
using reference = int&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
};
|
||||
}
|
||||
#endif //BITSERY_TRAITS_CORE_NOT_DEFINED_TYPE_H
|
||||
@@ -24,96 +24,167 @@
|
||||
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
|
||||
|
||||
#include <type_traits>
|
||||
#include "both_common.h"
|
||||
#include <utility>
|
||||
#include "adapter_utils.h"
|
||||
#include "../traits/core/traits.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
//this allows to call private serialize method for the class
|
||||
//just make friend it to that class
|
||||
struct Access {
|
||||
template<typename S, typename T>
|
||||
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
|
||||
obj.serialize(s);
|
||||
}
|
||||
};
|
||||
|
||||
//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 {
|
||||
|
||||
template<typename T, typename Enable = void>
|
||||
struct SAME_SIZE_UNSIGNED_TYPE {
|
||||
using type = typename std::make_unsigned<T>::type;
|
||||
//helper types for error handling
|
||||
template <typename T>
|
||||
struct IsContainerTraitsDefined:public IsDefined<typename traits::ContainerTraits<T>::TValue> {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||
using type = typename std::make_unsigned<typename std::underlying_type<T>::type>::type;
|
||||
template <typename T>
|
||||
struct IsTextTraitsDefined:public IsDefined<typename traits::TextTraits<T>::TValue> {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
|
||||
using type = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
|
||||
template <typename Ext, typename T>
|
||||
struct IsExtensionTraitsDefined:public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE<T>::type;
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct SerializeFunction {
|
||||
static void invoke(S &s, T &v) {
|
||||
static_assert(!std::is_void<Enabled>::value,
|
||||
"\nPlease define 'serialize' function for your type:\n"
|
||||
" template<typename S>\n"
|
||||
" void serialize(S& s, <YourType>& o)\n"
|
||||
" {\n"
|
||||
" ...\n"
|
||||
" }\n");
|
||||
}
|
||||
//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 S, typename T>
|
||||
struct SerializeFunction<S, T, typename std::enable_if<
|
||||
std::is_same<void, decltype(serialize(std::declval<S &>(), std::declval<T &>()))>::value
|
||||
>::type> {
|
||||
static void invoke(S &s, T &v) {
|
||||
serialize(s, v);
|
||||
}
|
||||
};
|
||||
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 {
|
||||
};
|
||||
|
||||
/*
|
||||
* delta functions
|
||||
* this includes all integral types floats and enums(except bool)
|
||||
*/
|
||||
|
||||
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;
|
||||
template<typename T>
|
||||
struct IsFundamentalType : std::integral_constant<bool,
|
||||
std::is_enum<T>::value
|
||||
|| std::is_floating_point<T>::value
|
||||
|| std::is_integral<T>::value> {
|
||||
};
|
||||
|
||||
template<typename T, typename Integral = void>
|
||||
struct IntegralFromFundamental {
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IntegralFromFundamental<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||
using TValue = typename std::underlying_type<T>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IntegralFromFundamental<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
|
||||
using TValue = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct UnsignedFromFundamental {
|
||||
using type = typename std::make_unsigned<typename IntegralFromFundamental<T>::TValue>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using SameSizeUnsigned = typename UnsignedFromFundamental<T>::type;
|
||||
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T>
|
||||
struct SerializeFunction {
|
||||
|
||||
static void invoke(S &s, T &v) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct ArchiveFunction {
|
||||
|
||||
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");
|
||||
|
||||
archiveProcess(s, std::forward<T>(obj));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
209
include/bitsery/details/sessions.h
Normal file
209
include/bitsery/details/sessions.h
Normal file
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// Created by fraillt on 17.10.5.
|
||||
//
|
||||
|
||||
#ifndef BITSERY_BUFFER_SESSIONS_H
|
||||
#define BITSERY_BUFFER_SESSIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include "adapter_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace session {
|
||||
/*
|
||||
* writer/reader implementations that disable session support
|
||||
*/
|
||||
template <typename TWriter>
|
||||
struct DisabledSessionsWriter {
|
||||
void begin(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void end(TWriter& ) {
|
||||
static_assert(std::is_void<TWriter>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
void flushSessions(TWriter& ) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct DisabledSessionsReader {
|
||||
template <typename TBufferContext>
|
||||
DisabledSessionsReader(TReader& , TBufferContext& ) {
|
||||
}
|
||||
|
||||
void begin() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
void end() {
|
||||
static_assert(std::is_void<TReader>::value, "Sessions is disabled, enable it via configuration");
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* writer/reader real implementations
|
||||
* sessions reading requires to have random access iterators, so it cannot be used with streams
|
||||
*/
|
||||
template <typename TWriter>
|
||||
class SessionsWriter {
|
||||
public:
|
||||
|
||||
void begin(TWriter& ) {
|
||||
//write position
|
||||
_sessionIndex.push(_sessions.size());
|
||||
_sessions.emplace_back(0);
|
||||
}
|
||||
|
||||
void end(TWriter& writer) {
|
||||
assert(!_sessionIndex.empty());
|
||||
//change position to session end
|
||||
auto sessionIt = std::next(std::begin(_sessions), _sessionIndex.top());
|
||||
_sessionIndex.pop();
|
||||
*sessionIt = writer.writtenBytesCount();
|
||||
}
|
||||
|
||||
void flushSessions(TWriter& writer) {
|
||||
if (_sessions.size()) {
|
||||
assert(_sessionIndex.empty());
|
||||
auto dataSize = writer.writtenBytesCount();
|
||||
for(auto& s:_sessions) {
|
||||
details::writeSize(writer, s);
|
||||
}
|
||||
_sessions.clear();
|
||||
|
||||
auto totalSize = writer.writtenBytesCount();
|
||||
//write offset where actual data ends
|
||||
auto sessionsOffset = totalSize - dataSize + 4;//4 bytes for offset data
|
||||
writer.template writeBytes<4>(static_cast<uint32_t>(sessionsOffset));
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<size_t> _sessions{};
|
||||
std::stack<size_t> _sessionIndex;
|
||||
};
|
||||
|
||||
template <typename TReader>
|
||||
struct SessionsReader {
|
||||
using TIterator = typename TReader::TIterator;
|
||||
|
||||
template <typename InputAdapter>
|
||||
SessionsReader(TReader& r, InputAdapter& adapter)
|
||||
:_reader{r},
|
||||
_beginIt{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_posItRef{details::SessionAccess::posIteratorRef<InputAdapter, TIterator>(adapter)},
|
||||
_endItRef{details::SessionAccess::endIteratorRef<InputAdapter, TIterator>(adapter)}
|
||||
{
|
||||
}
|
||||
void begin() {
|
||||
if (_sessions.empty()) {
|
||||
if (!initializeSessions())
|
||||
return;
|
||||
}
|
||||
|
||||
//save end position for current session
|
||||
_sessionsStack.push(_endItRef);
|
||||
if (_nextSessionIt != std::end(_sessions)) {
|
||||
if (std::distance(_posItRef, _endItRef) > 0) {
|
||||
//set end position for new session
|
||||
auto newEnd = std::next(_beginIt, *_nextSessionIt);
|
||||
if (std::distance(newEnd, _endItRef) < 0)
|
||||
{
|
||||
//new session cannot end further than current end
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return;
|
||||
}
|
||||
_endItRef = newEnd;
|
||||
++_nextSessionIt;
|
||||
}
|
||||
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
|
||||
} else {
|
||||
//there is no data to read anymore
|
||||
//pos == end or buffer overflow while session is active
|
||||
if (!(_posItRef == _endItRef || _reader.error() == ReaderError::NoError)) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void end() {
|
||||
if (!_sessionsStack.empty()) {
|
||||
//move position to the end of session
|
||||
//can additionaly be checked for session data versioning
|
||||
//_pos == _end : same versions
|
||||
//distance(_pos,_end) > 0: reading newer version
|
||||
//error() == BUFFER_OVERFLOW: reading older version
|
||||
auto dist = std::distance(_posItRef, _endItRef);
|
||||
if (dist > 0) {
|
||||
//newer version might have some inner sessions, try to find the one after current ends
|
||||
auto currPos = static_cast<size_t>(std::distance(_beginIt, _endItRef));
|
||||
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
|
||||
if (*_nextSessionIt > currPos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
_posItRef = _endItRef;
|
||||
//restore end position
|
||||
_endItRef = _sessionsStack.top();
|
||||
_sessionsStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasActiveSessions() const {
|
||||
return _sessionsStack.size() > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
TReader& _reader;
|
||||
TIterator _beginIt;
|
||||
TIterator& _posItRef;
|
||||
TIterator& _endItRef;
|
||||
std::vector<size_t> _sessions{};
|
||||
std::vector<size_t>::iterator _nextSessionIt{};
|
||||
std::stack<TIterator> _sessionsStack{};
|
||||
|
||||
bool initializeSessions() {
|
||||
//save current position
|
||||
auto currPos = _posItRef;
|
||||
//read size
|
||||
if (std::distance(_posItRef, _endItRef) < 4) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
auto endSessionsSizesIt = std::next(_endItRef, -4);
|
||||
_posItRef = endSessionsSizesIt;
|
||||
uint32_t sessionsOffset{};
|
||||
_reader.template readBytes<4>(sessionsOffset);
|
||||
|
||||
auto bufferSize = std::distance(_beginIt, _endItRef);
|
||||
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
|
||||
_reader.setError(ReaderError::InvalidData);
|
||||
return false;
|
||||
}
|
||||
//we can initialy resizes to this value, and we'll shrink it after reading
|
||||
//read session sizes
|
||||
auto sessionsIt = std::back_inserter(_sessions);
|
||||
_posItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
while (std::distance(_posItRef, endSessionsSizesIt) > 0) {
|
||||
size_t size;
|
||||
details::readSize(_reader, size, bufferSize);
|
||||
*sessionsIt++ = size;
|
||||
}
|
||||
_sessions.shrink_to_fit();
|
||||
//set iterators to data
|
||||
_posItRef = currPos;
|
||||
_endItRef = std::next(_endItRef, -static_cast<int32_t>(sessionsOffset));
|
||||
_nextSessionIt = std::begin(_sessions);//set before first session;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_BUFFER_SESSIONS_H
|
||||
@@ -1,165 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_DETAILS_TRAITS_H
|
||||
#define BITSERY_DETAILS_TRAITS_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
namespace bitsery {
|
||||
namespace details {
|
||||
|
||||
/*
|
||||
* helper traits that is used internaly, or by other traits
|
||||
*/
|
||||
template <typename T, typename = int>
|
||||
struct IsResizable : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct IsResizable <T, decltype((void)std::declval<T>().resize(1u), 0)> : std::true_type {};
|
||||
|
||||
/*
|
||||
* core library traits, used to extend library for custom types
|
||||
*/
|
||||
|
||||
//traits for extension
|
||||
template<typename Extension, typename T>
|
||||
struct ExtensionTraits {
|
||||
//this type is used, when using extesion without custom lambda
|
||||
// eg.: extension4b>(obj, myextension{}) will call s.value4b(obj) for TValue
|
||||
// or extesion(obj, myextension{}) will call s.object(obj) for TValue
|
||||
//when this is void, it will compile, but value and object overloads will do nothing.
|
||||
using TValue = void;
|
||||
|
||||
//does extension support ext<N>(...) syntax, by calling value<N> with TValue
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
//does extension support ext(...) syntax, by calling object with TValue
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
//does extension support ext(..., lambda)
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
|
||||
//primary traits for containers
|
||||
template<typename T>
|
||||
struct ContainerTraits {
|
||||
|
||||
using TValue = typename T::value_type;
|
||||
|
||||
//default behaviour is resizable if container has method T::resize(size_t)
|
||||
static constexpr bool isResizable = IsResizable<T>::value;
|
||||
|
||||
//resize function, called only if container is resizable
|
||||
static void resize(T& container, size_t size) {
|
||||
container.resize(size);
|
||||
}
|
||||
|
||||
//get container size
|
||||
static size_t size(const T& container) {
|
||||
return container.size();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//specialization for C style array
|
||||
template<typename T, size_t N>
|
||||
struct ContainerTraits<T[N]> {
|
||||
using TValue = T;
|
||||
static constexpr bool isResizable = IsResizable<T>::value;
|
||||
static void resize(T (&container)[N], size_t size) {
|
||||
}
|
||||
static size_t size(const T (&container)[N]) {
|
||||
return N;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//traits for text
|
||||
template<typename T>
|
||||
struct TextTraits {
|
||||
|
||||
static constexpr bool isResizable = true;
|
||||
|
||||
//resize is without null-terminated character as with std::string,
|
||||
//but null terminated character will always be written
|
||||
//if you container doesn't add null-terminated character automaticaly, resize it to size+1;
|
||||
static void resize(T& container, size_t size) {
|
||||
container.resize(size);
|
||||
}
|
||||
|
||||
//used for serialization to get text length
|
||||
//length is until null-terminated character, size and length might not be equal
|
||||
static size_t length(const T& container) {
|
||||
auto begin = std::begin(container);
|
||||
using TValue = typename std::decay<decltype(*begin)>::type;
|
||||
return std::char_traits<TValue>::length(std::addressof(*begin));
|
||||
}
|
||||
};
|
||||
|
||||
//text traits specialization for std::string
|
||||
//for std::string return length as size(), for faster performance, so we don't need to traverse string to find null-terminated characeter
|
||||
//although it is not correct behaviour, meaning that string might have null-terminated characters in the middle,
|
||||
//but in this case it your decision if you store buffer in string and serialize it as a text.
|
||||
template<typename ... Args>
|
||||
struct TextTraits<std::basic_string<Args...>> {
|
||||
|
||||
static constexpr bool isResizable = true;
|
||||
|
||||
//resize is without null-terminated character as with std::string,
|
||||
//but null terminated character will always be written
|
||||
//if you container doesn't add null-terminated character automaticaly, resize it to size+1;
|
||||
static void resize(std::basic_string<Args...>& container, size_t size) {
|
||||
container.resize(size);
|
||||
}
|
||||
|
||||
//used for serialization to get text length
|
||||
//length is until null-terminated character, size and length might not be equal
|
||||
static size_t length(const std::basic_string<Args...>& container) {
|
||||
return container.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//traits only for buffer reader/writer
|
||||
template <typename T>
|
||||
struct BufferContainerTraits: public ContainerTraits<T> {
|
||||
//this function is only used by BufferWriter, when writing data to buffer,
|
||||
//it is called only current buffer size is not enough to write.
|
||||
//it is used to dramaticaly improve performance by updating buffer directly
|
||||
//instead of using back_insert_iterator to append each byte to buffer.
|
||||
//thats why BufferWriter return range iterators
|
||||
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());
|
||||
}
|
||||
|
||||
using TDifference = typename T::difference_type;
|
||||
using TIterator = typename T::iterator;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_DETAILS_TRAITS_H
|
||||
@@ -46,24 +46,34 @@ namespace bitsery {
|
||||
class Entropy {
|
||||
public:
|
||||
|
||||
constexpr explicit Entropy(TContainer& values) : _values{values} {
|
||||
|
||||
/**
|
||||
* Allows entropy-encoding technique, by writing few bits for most common values
|
||||
* @param values list of most common values
|
||||
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing bits for index
|
||||
*/
|
||||
constexpr Entropy(TContainer& values, bool alignBeforeData=true)
|
||||
: _values{values},
|
||||
_alignBeforeData{alignBeforeData} {
|
||||
};
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &s, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
assert(details::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
auto index = details::findEntropyIndex(obj, _values);
|
||||
s.ext(index, ext::ValueRange<size_t>{0u, details::ContainerTraits<TContainer>::size(_values)});
|
||||
s.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
s.align();
|
||||
if (!index)
|
||||
fnc(const_cast<T &>(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &d, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
assert(details::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
|
||||
size_t index{};
|
||||
d.ext(index, ext::ValueRange<size_t>{0u, details::ContainerTraits<TContainer>::size(_values)});
|
||||
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
|
||||
if (_alignBeforeData)
|
||||
d.align();
|
||||
if (index)
|
||||
obj = *std::next(std::begin(_values), index-1);
|
||||
else
|
||||
@@ -72,10 +82,11 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
TContainer& _values;
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename TContainer, typename T>
|
||||
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
|
||||
using TValue = T;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Growable, T> {
|
||||
using TValue = T;
|
||||
|
||||
@@ -20,16 +20,18 @@
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#ifndef BITSERY_EXT_CONTAINER_MAP_H
|
||||
#define BITSERY_EXT_CONTAINER_MAP_H
|
||||
#ifndef BITSERY_EXT_STD_MAP_H
|
||||
#define BITSERY_EXT_STD_MAP_H
|
||||
|
||||
#include "../details/adapter_utils.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class ContainerMap {
|
||||
class StdMap {
|
||||
public:
|
||||
|
||||
constexpr explicit ContainerMap(size_t maxSize):_maxSize{maxSize} {}
|
||||
constexpr explicit StdMap(size_t maxSize):_maxSize{maxSize} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
@@ -65,9 +67,9 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ContainerMap, T> {
|
||||
struct ExtensionTraits<ext::StdMap, T> {
|
||||
using TValue = void;
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
@@ -78,4 +80,4 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_CONTAINER_MAP_H
|
||||
#endif //BITSERY_EXT_STD_MAP_H
|
||||
@@ -21,8 +21,8 @@
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#ifndef BITSERY_EXT_OPTIONAL_H
|
||||
#define BITSERY_EXT_OPTIONAL_H
|
||||
#ifndef BITSERY_EXT_STD_OPTIONAL_H
|
||||
#define BITSERY_EXT_STD_OPTIONAL_H
|
||||
|
||||
|
||||
//this module do not include optional, but expects it to be declared in std::optional
|
||||
@@ -40,9 +40,14 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
using std_optional = ::std::optional<T>;
|
||||
|
||||
class Optional {
|
||||
class StdOptional {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Works with std::optional types
|
||||
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing/reading bool state of optional
|
||||
*/
|
||||
explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {}
|
||||
template<typename T>
|
||||
constexpr void assertType() const {
|
||||
using TOpt = typename std::remove_cv<T>::type;
|
||||
@@ -54,7 +59,9 @@ namespace bitsery {
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const T &obj, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
ser.boolByte(static_cast<bool>(obj));
|
||||
ser.boolValue(static_cast<bool>(obj));
|
||||
if (_alignBeforeData)
|
||||
ser.align();
|
||||
if (obj)
|
||||
fnc(const_cast<typename T::value_type & >(*obj));
|
||||
}
|
||||
@@ -63,7 +70,9 @@ namespace bitsery {
|
||||
void deserialize(Des &des, Reader &, T &obj, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
bool exists{};
|
||||
des.boolByte(exists);
|
||||
des.boolValue(exists);
|
||||
if (_alignBeforeData)
|
||||
des.align();
|
||||
if (exists) {
|
||||
typename T::value_type tmp{};
|
||||
fnc(tmp);
|
||||
@@ -73,12 +82,14 @@ namespace bitsery {
|
||||
obj = T{};
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool _alignBeforeData;
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::Optional, T> {
|
||||
struct ExtensionTraits<ext::StdOptional, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
@@ -89,4 +100,4 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_OPTIONAL_H
|
||||
#endif //BITSERY_EXT_STD_OPTIONAL_H
|
||||
111
include/bitsery/ext/std_queue.h
Normal file
111
include/bitsery/ext/std_queue.h
Normal file
@@ -0,0 +1,111 @@
|
||||
//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_EXT_STD_QUEUE_H
|
||||
#define BITSERY_EXT_STD_QUEUE_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <queue>
|
||||
//include type traits for deque and vector, because they are defaults for queue and priority_queue
|
||||
#include "../traits/deque.h"
|
||||
#include "../traits/vector.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class StdQueue {
|
||||
private:
|
||||
//inherit from queue so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct QueueCnt : public std::queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&QueueCnt::c);
|
||||
}
|
||||
};
|
||||
//inherit from queue so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
};
|
||||
|
||||
size_t _maxSize;
|
||||
public:
|
||||
explicit StdQueue(size_t maxSize):_maxSize{maxSize} {};
|
||||
|
||||
//for queue
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::queue<T,C> &obj, Fnc &&fnc) const {
|
||||
ser.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::queue<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
//for priority_queue
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
ser.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::priority_queue<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(PriorityQueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdQueue, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_QUEUE_H
|
||||
97
include/bitsery/ext/std_set.h
Normal file
97
include/bitsery/ext/std_set.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//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_EXT_STD_SET_H
|
||||
#define BITSERY_EXT_STD_SET_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../details/adapter_utils.h"
|
||||
//we need this, so we could
|
||||
#include <unordered_set>
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class StdSet {
|
||||
public:
|
||||
|
||||
constexpr explicit StdSet(size_t maxSize):_maxSize{maxSize} {}
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename Fnc>
|
||||
void serialize(Ser &, Writer &writer, const T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
auto size = obj.size();
|
||||
assert(size <= _maxSize);
|
||||
details::writeSize(writer, size);
|
||||
|
||||
for (auto &v:obj)
|
||||
fnc(const_cast<TKey &>(v));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename Fnc>
|
||||
void deserialize(Des &, Reader &reader, T &obj, Fnc &&fnc) const {
|
||||
using TKey = typename T::key_type;
|
||||
|
||||
size_t size{};
|
||||
details::readSize(reader, size, _maxSize);
|
||||
auto hint = obj.begin();
|
||||
obj.clear();
|
||||
reserve(obj, size);
|
||||
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
TKey key;
|
||||
fnc(key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
template <typename T>
|
||||
void reserve(std::unordered_set<T>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(std::unordered_multiset<T>& obj, size_t size) const {
|
||||
obj.reserve(size);
|
||||
}
|
||||
template <typename T>
|
||||
void reserve(T obj, size_t size) const {
|
||||
//for ordered container do nothing
|
||||
}
|
||||
size_t _maxSize;
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdSet, T> {
|
||||
using TValue = typename T::key_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_SET_H
|
||||
82
include/bitsery/ext/std_stack.h
Normal file
82
include/bitsery/ext/std_stack.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//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_EXT_STD_STACK_H
|
||||
#define BITSERY_EXT_STD_STACK_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <stack>
|
||||
//include type traits for deque, because stack default underlying container is deque
|
||||
#include "../traits/deque.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
class StdStack {
|
||||
private:
|
||||
//inherit from stack so we could take underlying container
|
||||
template <typename T, typename C>
|
||||
struct StackCnt : public std::stack<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::stack<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
static C& getContainer(std::stack<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&StackCnt::c);
|
||||
}
|
||||
};
|
||||
size_t _maxSize;
|
||||
public:
|
||||
explicit StdStack(size_t maxSize):_maxSize{maxSize} {};
|
||||
|
||||
template<typename Ser, typename Writer, typename T, typename C, typename Fnc>
|
||||
void serialize(Ser &ser, Writer &, const std::stack<T,C> &obj, Fnc &&fnc) const {
|
||||
ser.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<typename Des, typename Reader, typename T, typename C, typename Fnc>
|
||||
void deserialize(Des &des, Reader &, std::stack<T,C> &obj, Fnc &&fnc) const {
|
||||
des.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::StdStack, T> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool SupportValueOverload = true;
|
||||
static constexpr bool SupportObjectOverload = true;
|
||||
static constexpr bool SupportLambdaOverload = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_EXT_STD_STACK_H
|
||||
@@ -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(BufferReaderError::INVALID_BUFFER_DATA);
|
||||
reader.setError(ReaderError::InvalidData);
|
||||
v = _range.min;
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace bitsery {
|
||||
};
|
||||
}
|
||||
|
||||
namespace details {
|
||||
namespace traits {
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ValueRange<T>, T> {
|
||||
using TValue = void;
|
||||
|
||||
110
include/bitsery/flexible.h
Normal file
110
include/bitsery/flexible.h
Normal file
@@ -0,0 +1,110 @@
|
||||
//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_FLEXIBLE_H
|
||||
#define BITSERY_FLEXIBLE_H
|
||||
|
||||
#include "details/serialization_common.h"
|
||||
#include "details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace flexible {
|
||||
|
||||
//overload when T is reference type
|
||||
template<typename S, typename T>
|
||||
void archiveProcessImpl(S &s, T &&head, std::true_type) {
|
||||
s.object(std::forward<T>(head));
|
||||
};
|
||||
|
||||
//overload when T is rvalue type, only allowable for behaviour modifying functions for deserializer
|
||||
template<typename S, typename T>
|
||||
void archiveProcessImpl(S &s, T &&head, std::false_type) {
|
||||
static_assert(std::is_base_of<ArchiveWrapperFnc, T>::value,
|
||||
"\nOnly archive behaviour modifying functions can be passed by rvalue to deserializer\n");
|
||||
serialize(s, head);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//define function that enables s.archive(....) usage
|
||||
template<typename S, typename T>
|
||||
void archiveProcess(S &s, T &&head) {
|
||||
flexible::archiveProcessImpl(s, std::forward<T>(head), std::is_reference<T>{});
|
||||
}
|
||||
|
||||
//wrapper functions that enables to serialize as container or string
|
||||
template<typename T, size_t N>
|
||||
flexible::CArray<T, N, true> asText(T (&str)[N]) {
|
||||
return {str};
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
flexible::CArray<T, N, false> asContainer(T (&obj)[N]) {
|
||||
return {obj};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
flexible::MaxSize<T> maxSize(T& obj, size_t max) {
|
||||
return {obj, max};
|
||||
}
|
||||
|
||||
//define serialize function for fundamental types
|
||||
template<typename S>
|
||||
void serialize(S &s, bool &v) {
|
||||
s.boolValue(v);
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
|
||||
void serialize(S &s, T &v) {
|
||||
s.template value<sizeof(T)>(v);
|
||||
};
|
||||
|
||||
//define serialization for c-style container
|
||||
|
||||
//if array is integral type, specify explicitly how to process: as text or container
|
||||
template<typename S, typename T, size_t N, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
void serialize(S &s, T (&obj)[N]) {
|
||||
static_assert(N == 0,
|
||||
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using c-style array with integral types\n");
|
||||
};
|
||||
|
||||
template<typename S, typename T, size_t N, typename std::enable_if<!std::is_integral<T>::value>::type * = nullptr>
|
||||
void serialize(S &s, T (&obj)[N]) {
|
||||
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
|
||||
37
include/bitsery/flexible/array.h
Normal file
37
include/bitsery/flexible/array.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
|
||||
#include "../traits/array.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, size_t N>
|
||||
void serialize(S &s, std::array<T, N> &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_ARRAY_H
|
||||
37
include/bitsery/flexible/deque.h
Normal file
37
include/bitsery/flexible/deque.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
|
||||
#include "../traits/deque.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::deque<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_DEQUE_H
|
||||
37
include/bitsery/flexible/forward_list.h
Normal file
37
include/bitsery/flexible/forward_list.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
|
||||
#include "../traits/forward_list.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::forward_list<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_FORWARD_LIST_H
|
||||
37
include/bitsery/flexible/list.h
Normal file
37
include/bitsery/flexible/list.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_LIST_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_LIST_H
|
||||
|
||||
#include "../traits/list.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::list<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_LIST_H
|
||||
54
include/bitsery/flexible/map.h
Normal file
54
include/bitsery/flexible/map.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//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_FLEXIBLE_TYPE_STD_MAP_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::map<TArgs...>::key_type;
|
||||
using TValue = typename std::map<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::multimap<TArgs...>::key_type;
|
||||
using TValue = typename std::multimap<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_MAP_H
|
||||
42
include/bitsery/flexible/queue.h
Normal file
42
include/bitsery/flexible/queue.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//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_FLEXIBLE_TYPE_STD_QUEUE_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_QUEUE_H
|
||||
|
||||
#include "../ext/std_queue.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename C>
|
||||
void serialize(S &s, std::queue<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdQueue{maxSize});
|
||||
}
|
||||
|
||||
template<typename S, typename T, typename C, typename Comp>
|
||||
void serialize(S &s, std::priority_queue<T, C, Comp> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdQueue{maxSize});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_QUEUE_H
|
||||
43
include/bitsery/flexible/set.h
Normal file
43
include/bitsery/flexible/set.h
Normal file
@@ -0,0 +1,43 @@
|
||||
//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_FLEXIBLE_TYPE_STD_SET_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_SET_H
|
||||
|
||||
#include <set>
|
||||
#include "../ext/std_set.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_SET_H
|
||||
36
include/bitsery/flexible/stack.h
Normal file
36
include/bitsery/flexible/stack.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//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_FLEXIBLE_TYPE_STD_STACK_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_STACK_H
|
||||
|
||||
#include "../ext/std_stack.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename C>
|
||||
void serialize(S &s, std::stack<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdStack{maxSize});
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_STACK_H
|
||||
37
include/bitsery/flexible/string.h
Normal file
37
include/bitsery/flexible/string.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_STRING_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_STRING_H
|
||||
|
||||
#include "../traits/string.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename T, typename ... TArgs>
|
||||
void serialize(S &s, std::basic_string<T, TArgs...> &str) {
|
||||
flexible::processContainer(s, str);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_STRING_H
|
||||
55
include/bitsery/flexible/unordered_map.h
Normal file
55
include/bitsery/flexible/unordered_map.h
Normal file
@@ -0,0 +1,55 @@
|
||||
//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_FLEXIBLE_TYPE_STD_UNORDERED_MAP_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_UNORDERED_MAP_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "../ext/std_map.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_map<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::unordered_map<TArgs...>::key_type;
|
||||
using TValue = typename std::unordered_map<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_multimap<TArgs ... > &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
using TKey = typename std::unordered_multimap<TArgs...>::key_type;
|
||||
using TValue = typename std::unordered_multimap<TArgs...>::mapped_type;
|
||||
s.ext(obj, ext::StdMap{maxSize},
|
||||
[&s](TKey& key, TValue& value) {
|
||||
s.object(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_UNORDERED_MAP_H
|
||||
43
include/bitsery/flexible/unordered_set.h
Normal file
43
include/bitsery/flexible/unordered_set.h
Normal file
@@ -0,0 +1,43 @@
|
||||
//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_FLEXIBLE_TYPE_STD_UNORDERED_SET_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_UNORDERED_SET_H
|
||||
|
||||
#include <unordered_set>
|
||||
#include "../ext/std_set.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_set<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::unordered_multiset<TArgs...> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
|
||||
s.ext(obj, ext::StdSet{maxSize});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_UNORDERED_SET_H
|
||||
37
include/bitsery/flexible/vector.h
Normal file
37
include/bitsery/flexible/vector.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//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_FLEXIBLE_TYPE_STD_VECTOR_H
|
||||
#define BITSERY_FLEXIBLE_TYPE_STD_VECTOR_H
|
||||
|
||||
#include "../traits/vector.h"
|
||||
#include "../details/flexible_common.h"
|
||||
|
||||
namespace bitsery {
|
||||
template<typename S, typename ... TArgs>
|
||||
void serialize(S &s, std::vector<TArgs... > &obj) {
|
||||
flexible::processContainer(s, obj);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_FLEXIBLE_TYPE_STD_VECTOR_H
|
||||
@@ -24,25 +24,41 @@
|
||||
#ifndef BITSERY_SERIALIZER_H
|
||||
#define BITSERY_SERIALIZER_H
|
||||
|
||||
#include "common.h"
|
||||
#include "details/serialization_common.h"
|
||||
#include "adapter_writer.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename Config>
|
||||
template<typename TAdapterWriter>
|
||||
class BasicSerializer {
|
||||
public:
|
||||
explicit BasicSerializer(BasicBufferWriter<Config> &w, void* context = nullptr) : _writter{w}, _context{context} {};
|
||||
//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>;
|
||||
|
||||
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&& ) = 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;
|
||||
}
|
||||
|
||||
@@ -59,24 +75,33 @@ namespace bitsery {
|
||||
fnc(const_cast<T& >(obj));
|
||||
};
|
||||
|
||||
/*
|
||||
* functionality, that enables simpler serialization syntax, by including additional header
|
||||
*/
|
||||
template<typename T, typename ... TArgs>
|
||||
void archive(T &&head, TArgs &&... tail) {
|
||||
//serialize object
|
||||
details::ArchiveFunction<BasicSerializer, T>::invoke(*this, std::forward<T>(head));
|
||||
//expand other elements
|
||||
archive(std::forward<TArgs>(tail)...);
|
||||
}
|
||||
|
||||
/*
|
||||
* value overloads
|
||||
*/
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
|
||||
void value(const T &v) {
|
||||
static_assert(std::numeric_limits<T>::is_iec559, "");
|
||||
_writter.template writeBytes<VSIZE>(reinterpret_cast<const details::SAME_SIZE_UNSIGNED<T> &>(v));
|
||||
using TValue = typename details::IntegralFromFundamental<T>::TValue;
|
||||
_writer.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
void value(const T &v) {
|
||||
_writter.template writeBytes<VSIZE>(reinterpret_cast<const typename std::underlying_type<T>::type &>(v));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
void value(const T &v) {
|
||||
_writter.template writeBytes<VSIZE>(v);
|
||||
/*
|
||||
* enable bit-packing
|
||||
*/
|
||||
template <typename Fnc>
|
||||
void enableBitPacking(Fnc&& fnc) {
|
||||
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -84,43 +109,39 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
void ext(const T &obj, Ext &&extension, Fnc &&fnc) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportLambdaOverload,
|
||||
void ext(const T &obj, const Ext &extension, Fnc &&fnc) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
|
||||
"extension doesn't support overload with lambda");
|
||||
extension.serialize(*this, _writter, obj, std::forward<Fnc>(fnc));
|
||||
extension.serialize(*this, _writer, obj, std::forward<Fnc>(fnc));
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
void ext(const T &obj, Ext &&extension) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportValueOverload,
|
||||
void ext(const T &obj, const Ext &extension) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
|
||||
"extension doesn't support overload with `value<N>`");
|
||||
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.serialize(*this, _writter, obj, [this](VType &v) { value<VSIZE>(v); });
|
||||
extension.serialize(*this, _writer, obj, [this](VType &v) { value<VSIZE>(v); });
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
void ext(const T &obj, Ext &&extension) {
|
||||
using ExtType = typename std::decay<Ext>::type;
|
||||
static_assert(details::ExtensionTraits<ExtType,T>::SupportObjectOverload,
|
||||
void ext(const T &obj, const Ext &extension) {
|
||||
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
|
||||
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
|
||||
"extension doesn't support overload with `object`");
|
||||
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
|
||||
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
|
||||
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
|
||||
extension.serialize(*this, _writter, obj, [this](VType &v) { object(v); });
|
||||
extension.serialize(*this, _writer, obj, [this](VType &v) { object(v); });
|
||||
};
|
||||
|
||||
/*
|
||||
* bool
|
||||
* boolValue
|
||||
*/
|
||||
|
||||
void boolBit(bool v) {
|
||||
_writter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
|
||||
}
|
||||
|
||||
void boolByte(bool v) {
|
||||
_writter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
|
||||
void boolValue(bool v) {
|
||||
procBoolValue(v, std::integral_constant<bool, TAdapterWriter::BitPackingEnabled>{});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -129,37 +150,20 @@ namespace bitsery {
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(const T &str, size_t maxSize) {
|
||||
static_assert(details::TextTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use text(const T&) overload without `maxSize` for static container");
|
||||
auto size = details::TextTraits<T>::length(str);
|
||||
//size can be equal to maxSize
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writter, size);
|
||||
auto begin = std::begin(str);
|
||||
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
|
||||
procText<VSIZE>(str, maxSize);
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void text(const T &str) {
|
||||
static_assert(!details::TextTraits<T>::isResizable,
|
||||
static_assert(details::IsTextTraitsDefined<T>::value,
|
||||
"Please define TextTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use text(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
auto size = details::TextTraits<T>::length(str);
|
||||
auto begin = std::begin(str);
|
||||
auto end = std::end(str);
|
||||
//size must be less than container capacity, because we need to store null-terminated character
|
||||
assert(size < std::distance(begin, end));
|
||||
details::writeSize(_writter, size);
|
||||
|
||||
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
void text(const T (&str)[N]) {
|
||||
auto size = details::TextTraits<T[N]>::length(str);
|
||||
assert(size < N);
|
||||
details::writeSize(_writter, size);
|
||||
auto begin = std::begin(str);
|
||||
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
|
||||
procText<VSIZE>(str, traits::ContainerTraits<T>::size(str));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -170,34 +174,39 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
void container(const T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, Fnc) overload without `maxSize` for static containers");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writter, size);
|
||||
details::writeSize(_writer, size);
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(const T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&) overload without `maxSize` for static containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writter, size);
|
||||
//todo optimisation is possible for contigous containers, but currently there is no compile-time check for this
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
|
||||
details::writeSize(_writer, size);
|
||||
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(const T &obj, size_t maxSize) {
|
||||
static_assert(details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&) overload without `maxSize` for static containers");
|
||||
auto size = details::ContainerTraits<T>::size(obj);
|
||||
auto size = traits::ContainerTraits<T>::size(obj);
|
||||
assert(size <= maxSize);
|
||||
details::writeSize(_writter, size);
|
||||
details::writeSize(_writer, size);
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
@@ -205,47 +214,34 @@ namespace bitsery {
|
||||
|
||||
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
|
||||
void container(const T &obj, Fnc &&fnc) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
void container(const T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
static_assert(VSIZE > 0, "");
|
||||
//todo optimisation is possible for contigous containers, but currently there is no compile-time check for this
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
|
||||
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void container(const T &obj) {
|
||||
static_assert(!details::ContainerTraits<T>::isResizable,
|
||||
static_assert(details::IsContainerTraitsDefined<T>::value,
|
||||
"Please define ContainerTraits or include from <bitsery/traits/...>");
|
||||
static_assert(!traits::ContainerTraits<T>::isResizable,
|
||||
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
}
|
||||
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
void container(const T (&arr)[N], Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
void container(const T (&arr)[N]) {
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container(const T (&arr)[N]) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
}
|
||||
|
||||
void align() {
|
||||
_writter.align();
|
||||
_writer.align();
|
||||
}
|
||||
|
||||
//overloads for functions with explicit type size
|
||||
@@ -283,14 +279,14 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text1b(const T (&str)[N]) { text<1, T, N>(str); }
|
||||
template<typename T>
|
||||
void text1b(const T &str) { text<1>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text2b(const T (&str)[N]) { text<2, T, N>(str); }
|
||||
template<typename T>
|
||||
void text2b(const T &str) { text<2>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void text4b(const T (&str)[N]) { text<4, T, N>(str); }
|
||||
template<typename T>
|
||||
void text4b(const T &str) { text<4>(str); }
|
||||
|
||||
template<typename T>
|
||||
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
|
||||
@@ -316,20 +312,10 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container1b(const T (&arr)[N]) { container<1>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container2b(const T (&arr)[N]) { container<2>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container4b(const T (&arr)[N]) { container<4>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
void container8b(const T (&arr)[N]) { container<8>(arr); }
|
||||
|
||||
private:
|
||||
BasicBufferWriter<Config> &_writter;
|
||||
friend AdapterAccess;
|
||||
|
||||
TAdapterWriter _writer;
|
||||
void* _context;
|
||||
|
||||
//process value types
|
||||
@@ -344,7 +330,11 @@ namespace bitsery {
|
||||
//true_type means, that we can copy whole buffer
|
||||
template<size_t VSIZE, typename It>
|
||||
void procContainer(It first, It last, std::true_type) {
|
||||
_writter.template writeBuffer<VSIZE>(&(*first), std::distance(first, last));
|
||||
using TValue = typename std::decay<decltype(*first)>::type;
|
||||
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
|
||||
if (first != last)
|
||||
_writer.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)),
|
||||
static_cast<size_t>(std::distance(first, last)));
|
||||
};
|
||||
|
||||
//process by calling functions
|
||||
@@ -356,6 +346,16 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
//process text,
|
||||
template<size_t VSIZE, typename T>
|
||||
void procText(const T& str, size_t maxSize) {
|
||||
auto length = traits::TextTraits<T>::length(str);
|
||||
assert((length + (traits::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
|
||||
details::writeSize(_writer, length);
|
||||
auto begin = std::begin(str);
|
||||
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
|
||||
};
|
||||
|
||||
//process object types
|
||||
template<typename It>
|
||||
void procContainer(It first, It last) {
|
||||
@@ -363,6 +363,28 @@ namespace bitsery {
|
||||
object(*first);
|
||||
};
|
||||
|
||||
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
|
||||
void procBoolValue(bool v, std::true_type) {
|
||||
_writer.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
|
||||
}
|
||||
|
||||
void procBoolValue(bool v, std::false_type) {
|
||||
_writer.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
|
||||
}
|
||||
|
||||
//enable bit-packing or do nothing if it is already enabled
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
|
||||
fnc(*this);
|
||||
}
|
||||
|
||||
template <typename Fnc>
|
||||
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
|
||||
//create serializer using bitpacking wrapper
|
||||
BasicSerializer<AdapterWriterBitPackingWrapper<TAdapterWriter>> tmp(_writer, _context);
|
||||
fnc(tmp);
|
||||
}
|
||||
|
||||
//these are dummy functions for extensions that have TValue = void
|
||||
void object(const details::DummyType&) {
|
||||
|
||||
@@ -373,10 +395,34 @@ namespace bitsery {
|
||||
|
||||
}
|
||||
|
||||
//dummy function, that stops archive variadic arguments expansion
|
||||
void archive() {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//helper type
|
||||
using Serializer = BasicSerializer<DefaultConfig>;
|
||||
template <typename Adapter>
|
||||
using Serializer = BasicSerializer<AdapterWriter<Adapter, DefaultConfig>>;
|
||||
|
||||
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
|
||||
template <typename Adapter, typename T>
|
||||
size_t quickSerialization(Adapter adapter, const T& value) {
|
||||
Serializer<Adapter> ser{std::move(adapter)};
|
||||
ser.object(value);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
size_t quickMeasureSize(const T& value) {
|
||||
BasicSerializer<MeasureSize> ser {nullptr};
|
||||
ser.object(value);
|
||||
auto& w = AdapterAccess::getWriter(ser);
|
||||
w.flush();
|
||||
return w.writtenBytesCount();
|
||||
}
|
||||
|
||||
}
|
||||
#endif //BITSERY_SERIALIZER_H
|
||||
|
||||
44
include/bitsery/traits/array.h
Normal file
44
include/bitsery/traits/array.h
Normal file
@@ -0,0 +1,44 @@
|
||||
//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_TRAITS_STD_ARRAY_H
|
||||
#define BITSERY_TRAITS_STD_ARRAY_H
|
||||
|
||||
#include "core/std_defaults.h"
|
||||
#include <array>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
template<typename T, size_t N>
|
||||
struct ContainerTraits<std::array<T, N>>
|
||||
:public StdContainer<std::array<T, N>, false, true> {};
|
||||
|
||||
template<typename T, size_t N>
|
||||
struct BufferAdapterTraits<std::array<T, N>>
|
||||
:public StdContainerForBufferAdapter<std::array<T, N>> {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_TYPE_TRAITS_STD_ARRAY_H
|
||||
85
include/bitsery/traits/core/std_defaults.h
Normal file
85
include/bitsery/traits/core/std_defaults.h
Normal file
@@ -0,0 +1,85 @@
|
||||
//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_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
#define BITSERY_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
|
||||
#include "traits.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace bitsery {
|
||||
namespace traits {
|
||||
|
||||
/*
|
||||
* these are helper types, to easier write specializations for std types
|
||||
*/
|
||||
|
||||
template<typename T, bool Resizable, bool Contiguous>
|
||||
struct StdContainer {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool isResizable = Resizable;
|
||||
static constexpr bool isContiguous = Contiguous;
|
||||
static size_t size(const T& container) {
|
||||
return container.size();
|
||||
}
|
||||
};
|
||||
|
||||
//specialization for resizable
|
||||
template<typename T, bool Contiguous>
|
||||
struct StdContainer<T, true, Contiguous> {
|
||||
using TValue = typename T::value_type;
|
||||
static constexpr bool isResizable = true;
|
||||
static constexpr bool isContiguous = Contiguous;
|
||||
static size_t size(const T& container) {
|
||||
return container.size();
|
||||
}
|
||||
static void resize(T& container, size_t size) {
|
||||
container.resize(size);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, bool Resizable = ContainerTraits<T>::isResizable>
|
||||
struct StdContainerForBufferAdapter {
|
||||
using TIterator = typename T::iterator;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
//specialization for resizable buffers
|
||||
template <typename T>
|
||||
struct StdContainerForBufferAdapter<T, true> {
|
||||
|
||||
static void increaseBufferSize(T& container) {
|
||||
//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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_HELPER_STD_DEFAULTS_H
|
||||
159
include/bitsery/traits/core/traits.h
Normal file
159
include/bitsery/traits/core/traits.h
Normal file
@@ -0,0 +1,159 @@
|
||||
//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_TRAITS_CORE_TRAITS_H
|
||||
#define BITSERY_TRAITS_CORE_TRAITS_H
|
||||
|
||||
#include <type_traits>
|
||||
#include "../../details/not_defined_type.h"
|
||||
|
||||
namespace bitsery {
|
||||
namespace traits {
|
||||
|
||||
/*
|
||||
* core library traits, used to extend library for custom types
|
||||
*/
|
||||
|
||||
//traits for extension
|
||||
template<typename Extension, typename T>
|
||||
struct ExtensionTraits {
|
||||
//this type is used, when using extesion without custom lambda
|
||||
// eg.: extension4b>(obj, myextension{}) will call s.value4b(obj) for TValue
|
||||
// or extesion(obj, myextension{}) will call s.object(obj) for TValue
|
||||
//when this is void, it will compile, but value and object overloads will do nothing.
|
||||
using TValue = details::NotDefinedType;
|
||||
|
||||
//does extension support ext<N>(...) syntax, by calling value<N> with TValue
|
||||
static constexpr bool SupportValueOverload = false;
|
||||
//does extension support ext(...) syntax, by calling object with TValue
|
||||
static constexpr bool SupportObjectOverload = false;
|
||||
//does extension support ext(..., lambda)
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
//primary traits for containers
|
||||
template<typename T>
|
||||
struct ContainerTraits {
|
||||
|
||||
using TValue = details::NotDefinedType;
|
||||
|
||||
static constexpr bool isResizable = false;
|
||||
//contiguous arrays has oppurtunity to memcpy whole buffer directly when using funtamental types
|
||||
//contiguous doesn't nesessary equal to random access iterator.
|
||||
//contiguous hopefully will be available in c++20
|
||||
static constexpr bool isContiguous = false;
|
||||
//resize function, called only if container is resizable
|
||||
static void resize(T& container, size_t size) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
|
||||
}
|
||||
//get container size
|
||||
static size_t size(const T& container) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
|
||||
return 0u;
|
||||
}
|
||||
};
|
||||
|
||||
//specialization for C style array
|
||||
template<typename T, size_t N>
|
||||
struct ContainerTraits<T[N]> {
|
||||
using TValue = T;
|
||||
static constexpr bool isResizable = false;
|
||||
static constexpr bool isContiguous = true;
|
||||
static size_t size(const T (&container)[N]) {
|
||||
return N;
|
||||
}
|
||||
};
|
||||
|
||||
//specialization for initializer list, even though it cannot be deserialized to.
|
||||
template<typename T>
|
||||
struct ContainerTraits<std::initializer_list<T>> {
|
||||
using TValue = T;
|
||||
static constexpr bool isResizable = false;
|
||||
static constexpr bool isContiguous = true;
|
||||
static size_t size(const std::initializer_list<T>& container) {
|
||||
return container.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//traits for text, default adds null-terminated character at the end
|
||||
template<typename T>
|
||||
struct TextTraits {
|
||||
using TValue = details::NotDefinedType;
|
||||
//if container is not null-terminated by default, add NUL at the end
|
||||
static constexpr bool addNUL = true;
|
||||
|
||||
//get length of null terminated container
|
||||
static size_t length(const T& container) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Define TextTraits or include from <bitsery/traits/...> to use as text");
|
||||
return 0u;
|
||||
}
|
||||
};
|
||||
|
||||
//traits only for buffer adapters
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits {
|
||||
//this function is only applies to resizable containers
|
||||
|
||||
//this function is only used by Writer, when writing data to buffer,
|
||||
//it is called only current buffer size is not enough to write.
|
||||
//it is used to dramaticaly improve performance by updating buffer directly
|
||||
//instead of using back_insert_iterator to append each byte to buffer.
|
||||
//thats why Writer return range iterators
|
||||
|
||||
static void increaseBufferSize(T& container) {
|
||||
static_assert(std::is_void<T>::value,
|
||||
"Define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer adapter container");
|
||||
}
|
||||
|
||||
using TIterator = details::NotDefinedType;
|
||||
using TValue = typename ContainerTraits<T>::TValue;
|
||||
};
|
||||
|
||||
//specialization for c-style buffer
|
||||
template <typename T, size_t N>
|
||||
struct BufferAdapterTraits<T[N]> {
|
||||
using TIterator = T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
//specialization for pointer type buffer
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<const T*> {
|
||||
using TIterator = const T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BufferAdapterTraits<T*> {
|
||||
using TIterator = T*;
|
||||
using TValue = T;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_CORE_TRAITS_H
|
||||
42
include/bitsery/traits/deque.h
Normal file
42
include/bitsery/traits/deque.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//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_TRAITS_STD_DEQUE_H
|
||||
#define BITSERY_TRAITS_STD_DEQUE_H
|
||||
|
||||
#include "core/std_defaults.h"
|
||||
#include <deque>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::deque<TArgs...>>
|
||||
: public StdContainer<std::deque<TArgs...>, true, false> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_STD_DEQUE_H
|
||||
49
include/bitsery/traits/forward_list.h
Normal file
49
include/bitsery/traits/forward_list.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//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_TRAITS_STD_FORWARD_LIST_H
|
||||
#define BITSERY_TRAITS_STD_FORWARD_LIST_H
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::forward_list<TArgs...>> {
|
||||
using TValue = typename std::forward_list<TArgs...>::value_type;
|
||||
static constexpr bool isResizable = true;
|
||||
static constexpr bool isContiguous = false;
|
||||
static size_t size(const std::forward_list<TArgs...>& container) {
|
||||
return static_cast<size_t>(std::distance(container.begin(), container.end()));
|
||||
}
|
||||
static void resize(std::forward_list<TArgs...>& container, size_t size) {
|
||||
container.resize(size);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_TRAITS_STD_FORWARD_LIST_H
|
||||
42
include/bitsery/traits/list.h
Normal file
42
include/bitsery/traits/list.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//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_TRAITS_STD_LIST_H
|
||||
#define BITSERY_TRAITS_STD_LIST_H
|
||||
|
||||
#include "core/std_defaults.h"
|
||||
#include <list>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::list<TArgs...>>
|
||||
: public StdContainer<std::list<TArgs...>, true, false> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_STD_LIST_H
|
||||
71
include/bitsery/traits/string.h
Normal file
71
include/bitsery/traits/string.h
Normal file
@@ -0,0 +1,71 @@
|
||||
//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_TRAITS_STD_STRING_H
|
||||
#define BITSERY_TRAITS_STD_STRING_H
|
||||
|
||||
#include "core/std_defaults.h"
|
||||
#include <string>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
|
||||
// specialization for string, because string is already included for std::char_traits
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainer<std::basic_string<TArgs...>, true, true> {};
|
||||
|
||||
template <typename ... TArgs>
|
||||
struct TextTraits<std::basic_string<TArgs...>> {
|
||||
using TValue = typename ContainerTraits<std::basic_string<TArgs...>>::TValue;
|
||||
//string is automatically null-terminated
|
||||
static constexpr bool addNUL = false;
|
||||
|
||||
//is is not 100% accurate, but for performance reasons assume that string stores text, not binary data
|
||||
static size_t length(const std::basic_string<TArgs...>& str) {
|
||||
return str.size();
|
||||
}
|
||||
};
|
||||
|
||||
//specialization for c-array
|
||||
template <typename T, size_t N>
|
||||
struct TextTraits<T[N]> {
|
||||
using TValue = T;
|
||||
static constexpr bool addNUL = true;
|
||||
|
||||
static size_t length(const T (&container)[N]) {
|
||||
return std::char_traits<T>::length(container);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferAdapterTraits<std::basic_string<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::basic_string<TArgs...>> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_VECTOR_H
|
||||
50
include/bitsery/traits/vector.h
Normal file
50
include/bitsery/traits/vector.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//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_TRAITS_STD_VECTOR_H
|
||||
#define BITSERY_TRAITS_STD_VECTOR_H
|
||||
|
||||
#include "core/std_defaults.h"
|
||||
#include <vector>
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
namespace traits {
|
||||
template<typename ... TArgs>
|
||||
struct ContainerTraits<std::vector<TArgs...>>
|
||||
:public StdContainer<std::vector<TArgs...>, true, true> {};
|
||||
|
||||
//bool vector is not contiguous, do not copy it directly to buffer
|
||||
template<typename Allocator>
|
||||
struct ContainerTraits<std::vector<bool, Allocator>>
|
||||
:public StdContainer<std::vector<bool, Allocator>, true, false> {};
|
||||
|
||||
template<typename ... TArgs>
|
||||
struct BufferAdapterTraits<std::vector<TArgs...>>
|
||||
:public StdContainerForBufferAdapter<std::vector<TArgs...>> {};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif //BITSERY_TRAITS_STD_VECTOR_H
|
||||
@@ -44,7 +44,7 @@ file(GLOB TestSourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(WARNING "extension tests for optional is disable for VS, because VS currenty doesn't have <optional>")
|
||||
list(REMOVE_ITEM TestSourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/serialization_ext_optional.cpp)
|
||||
list(REMOVE_ITEM TestSourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/serialization_ext_std_optional.cpp)
|
||||
endif()
|
||||
|
||||
include(${ExtCMakeFilesDir}/LinkTestLib.cmake)
|
||||
@@ -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}>)
|
||||
|
||||
109
tests/adapter_stream.cpp
Normal file
109
tests/adapter_stream.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/adapter/stream.h>
|
||||
#include <bitsery/adapter_writer.h>
|
||||
#include <bitsery/adapter_reader.h>
|
||||
#include <sstream>
|
||||
|
||||
//some helper types
|
||||
using Stream = std::stringstream;
|
||||
using OutputAdapter = bitsery::OutputStreamAdapter;
|
||||
using InputAdapter = bitsery::InputStreamAdapter ;
|
||||
using Writer = bitsery::AdapterWriter<bitsery::OutputStreamAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<bitsery::InputStreamAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
TEST(AdapterIOStream, WrittenBytesCountReturns0) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
EXPECT_THAT(buf.str().size(), Eq(1));
|
||||
EXPECT_THAT(w.writtenBytesCount(), Eq(0));
|
||||
}
|
||||
|
||||
TEST(AdapterIOStream, CorrectlyReturnsIsCompletedSuccessfully) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
}
|
||||
|
||||
TEST(AdapterIOStream, ReadingMoreThanAvailableReturnsZero) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
r.readBytes<1>(r1);
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
}
|
||||
|
||||
//this is strange, but probably stringstream doesnt use any of the base methods that sets io_base::iostate flags
|
||||
TEST(AdapterIOStream, WhenReadingStringStreamThenErrorCodeAlwaysReturnsNoError) {
|
||||
//setup data
|
||||
uint8_t t1 = 111;
|
||||
|
||||
Stream buf{};
|
||||
Writer w{{buf}};
|
||||
w.writeBytes<1>(t1);
|
||||
w.flush();
|
||||
|
||||
Reader r{{buf}};
|
||||
|
||||
uint8_t r1{};
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(false));
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::NoError));
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r.isCompletedSuccessfully(), Eq(true));
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
r.readBytes<1>(r1);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
//should by overflow error, but it all iostate flags are set to false...
|
||||
EXPECT_THAT(r.error(), Eq(bitsery::ReaderError::NoError));
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint8_t c;
|
||||
uint8_t d;
|
||||
uint64_t e;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t getBits(T v) {
|
||||
return bitsery::details::calcRequiredBits<T>({}, v);
|
||||
};
|
||||
|
||||
// *** bits operations
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
bw.writeBits(std::numeric_limits<uint64_t>::max(), 64);
|
||||
bw.writeBits(std::numeric_limits<uint32_t>::max(), 32);
|
||||
bw.writeBits(std::numeric_limits<uint16_t>::max(), 16);
|
||||
bw.writeBits(std::numeric_limits<uint8_t>::max(), 8);
|
||||
bw.flush();
|
||||
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
uint64_t v64{};
|
||||
uint32_t v32{};
|
||||
uint16_t v16{};
|
||||
uint8_t v8{};
|
||||
br.readBits(v64, 64);
|
||||
br.readBits(v32, 32);
|
||||
br.readBits(v16, 16);
|
||||
br.readBits(v8, 8);
|
||||
|
||||
EXPECT_THAT(v64, Eq(std::numeric_limits<uint64_t>::max()));
|
||||
EXPECT_THAT(v32, Eq(std::numeric_limits<uint32_t>::max()));
|
||||
EXPECT_THAT(v16, Eq(std::numeric_limits<uint16_t>::max()));
|
||||
EXPECT_THAT(v8, Eq(std::numeric_limits<uint8_t>::max()));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
|
||||
//setup data
|
||||
constexpr IntegralUnsignedTypes data{
|
||||
485454,//bits 19
|
||||
45978,//bits 16
|
||||
0,//bits 1
|
||||
36,//bits 6
|
||||
479845648946//bits 39
|
||||
};
|
||||
|
||||
constexpr size_t aBITS = getBits(data.a) + 2;
|
||||
constexpr size_t bBITS = getBits(data.b) + 0;
|
||||
constexpr size_t cBITS = getBits(data.c) + 2;
|
||||
constexpr size_t dBITS = getBits(data.d) + 1;
|
||||
constexpr size_t eBITS = getBits(data.e) + 8;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.writeBits(data.a, aBITS);
|
||||
bw.writeBits(data.b, bBITS);
|
||||
bw.writeBits(data.c, cBITS);
|
||||
bw.writeBits(data.d, dBITS);
|
||||
bw.writeBits(data.e, eBITS);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(bytesCount));
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
IntegralUnsignedTypes res;
|
||||
|
||||
br.readBits(res.a, aBITS);
|
||||
br.readBits(res.b, bBITS);
|
||||
br.readBits(res.c, cBITS);
|
||||
br.readBits(res.d, dBITS);
|
||||
br.readBits(res.e, eBITS);
|
||||
|
||||
EXPECT_THAT(res.a, Eq(data.a));
|
||||
EXPECT_THAT(res.b, Eq(data.b));
|
||||
EXPECT_THAT(res.c, Eq(data.c));
|
||||
EXPECT_THAT(res.d, Eq(data.d));
|
||||
EXPECT_THAT(res.e, Eq(data.e));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, BufferSizeIsCountedPerByteNotPerBit) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.writeBits(7u,3);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1));
|
||||
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
uint16_t tmp;
|
||||
br.readBits(tmp,4);
|
||||
br.readBits(tmp,2);
|
||||
br.readBits(tmp,2);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
br.readBits(tmp,2);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
|
||||
//part of next byte
|
||||
BufferReader br1{range};
|
||||
br1.readBits(tmp,2);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
br1.readBits(tmp,7);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
|
||||
//bigger than byte
|
||||
BufferReader br2{range};
|
||||
br2.readBits(tmp,9);
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));//false
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
bw.writeBits(3u, 2);
|
||||
//3 calls to align after 1st data
|
||||
bw.align();
|
||||
bw.align();
|
||||
bw.align();
|
||||
bw.writeBits(7u, 3);
|
||||
//1 call to align after 2nd data
|
||||
bw.align();
|
||||
bw.writeBits(15u, 4);
|
||||
bw.flush();
|
||||
|
||||
unsigned char tmp;
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
br.readBits(tmp,2);
|
||||
EXPECT_THAT(tmp, Eq(3u));
|
||||
br.align();
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
br.readBits(tmp,3);
|
||||
br.align();
|
||||
br.align();
|
||||
br.align();
|
||||
EXPECT_THAT(tmp, Eq(7u));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
|
||||
br.readBits(tmp,4);
|
||||
EXPECT_THAT(tmp, Eq(15u));
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
BufferWriter bw{buf};
|
||||
|
||||
//write 2 bits and align
|
||||
bw.writeBits(3u, 2);
|
||||
bw.align();
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1));
|
||||
unsigned char tmp;
|
||||
BufferReader br1{range};
|
||||
br1.readBits(tmp,2);
|
||||
//read aligned bits
|
||||
br1.readBits(tmp,6);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
|
||||
BufferReader br2{range};
|
||||
//read 2 bits
|
||||
br2.readBits(tmp,2);
|
||||
br2.align();
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
}
|
||||
|
||||
|
||||
// *** bytes operations
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
uint32_t b;
|
||||
int16_t c;
|
||||
uint8_t d;
|
||||
int8_t e;
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.a = -4894541654564;
|
||||
data.b = 94545646;
|
||||
data.c = -8778;
|
||||
data.d = 200;
|
||||
data.e = -98;
|
||||
data.f[0] = 43;
|
||||
data.f[1] = -45;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.writeBytes<8>(data.a);
|
||||
bw.writeBytes<1>(data.e);
|
||||
bw.writeBuffer<1>(data.f, 2);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(18));
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<4>(res.b);
|
||||
br.readBytes<2>(res.c);
|
||||
br.readBytes<1>(res.d);
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<1>(res.e);
|
||||
br.readBuffer<1>(res.f, 2);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
//assert results
|
||||
|
||||
EXPECT_THAT(data.a, Eq(res.a));
|
||||
EXPECT_THAT(data.b, Eq(res.b));
|
||||
EXPECT_THAT(data.c, Eq(res.c));
|
||||
EXPECT_THAT(data.d, Eq(res.d));
|
||||
EXPECT_THAT(data.e, Eq(res.e));
|
||||
EXPECT_THAT(data.f, ContainerEq(res.f));
|
||||
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ReadWriteBufferFncCanAcceptSignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br1{bw.getWrittenRange()};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
bw.writeBits(15u, 4);
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.writeBits(12u, 4);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(sizeof(src) + 1));
|
||||
|
||||
//read from buffer
|
||||
BufferReader br1{range};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
uint8_t tmp{};
|
||||
br1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(15));
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
br1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(12));
|
||||
}
|
||||
|
||||
TEST(BufferBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) {
|
||||
//setup data
|
||||
int16_t data[2]{0x0000, 0x7FFF};
|
||||
int16_t res[2]{};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
bw.writeBits(2u, 2);
|
||||
bw.writeBytes<2>(data[0]);
|
||||
bw.writeBytes<2>(data[1]);
|
||||
bw.align();
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
|
||||
//read from buffer
|
||||
BufferReader br{range};
|
||||
uint8_t tmp{};
|
||||
br.readBits(tmp, 2);
|
||||
EXPECT_THAT(tmp, Eq(2));
|
||||
br.readBytes<2>(res[0]);
|
||||
br.readBytes<2>(res[1]);
|
||||
br.align();
|
||||
EXPECT_THAT(res[0], Eq(data[0]));
|
||||
EXPECT_THAT(res[1], Eq(data[1]));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include <bitsery/details/serialization_common.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
using bitsery::DefaultConfig;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct FixedBufferConfig: public DefaultConfig {
|
||||
using BufferType = std::array<uint8_t, 100>;
|
||||
};
|
||||
|
||||
struct NonFixedBufferConfig: public DefaultConfig {
|
||||
using BufferType = std::vector<uint8_t>;
|
||||
};
|
||||
|
||||
template <typename Config>
|
||||
class BufferWriting:public testing::Test {
|
||||
public:
|
||||
using type = Config;
|
||||
|
||||
};
|
||||
|
||||
using BufferWritingConfigs = ::testing::Types<
|
||||
NonFixedBufferConfig,
|
||||
FixedBufferConfig>;
|
||||
|
||||
TYPED_TEST_CASE(BufferWriting, BufferWritingConfigs);
|
||||
|
||||
static constexpr size_t DATA_SIZE = 14u;
|
||||
|
||||
template <typename BW>
|
||||
void writeData(BW& bw) {
|
||||
uint16_t tmp1{45}, tmp2{6543}, tmp3{46533};
|
||||
uint32_t tmp4{8979445}, tmp5{7987564};
|
||||
bw.template writeBytes<2>(tmp1);
|
||||
bw.template writeBytes<2>(tmp2);
|
||||
bw.template writeBytes<2>(tmp3);
|
||||
bw.template writeBytes<4>(tmp4);
|
||||
bw.template writeBytes<4>(tmp5);
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, GetWrittenRangeReturnsBeginEndIterators) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
writeData(bw);
|
||||
bw.flush();
|
||||
auto range = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range.begin(), range.end()), DATA_SIZE);
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
bw.writeBits(3u, 2);
|
||||
auto range1 = bw.getWrittenRange();
|
||||
bw.flush();
|
||||
auto range2 = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(0));
|
||||
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(BufferWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using Config = typename TestFixture::type;
|
||||
using Buffer = typename Config::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<Config> bw{buf};
|
||||
|
||||
bw.writeBits(3u, 2);
|
||||
bw.align();
|
||||
auto range1 = bw.getWrittenRange();
|
||||
bw.flush();
|
||||
auto range2 = bw.getWrittenRange();
|
||||
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(1));
|
||||
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
|
||||
}
|
||||
|
||||
TEST(BufferWrittingNonFixedBuffer, BufferIsAlwaysResizedToCapacity) {
|
||||
using Buffer = typename NonFixedBufferConfig::BufferType;
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<NonFixedBufferConfig> bw{buf};
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
uint32_t tmp{};
|
||||
bw.writeBytes<4>(tmp);
|
||||
bw.writeBytes<4>(tmp);
|
||||
EXPECT_TRUE(buf.size() == buf.capacity());
|
||||
}
|
||||
}
|
||||
@@ -22,15 +22,15 @@
|
||||
|
||||
|
||||
#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"
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
using bitsery::DefaultConfig;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
constexpr EndiannessType getInverseEndianness(EndiannessType e) {
|
||||
return e == EndiannessType::LittleEndian
|
||||
@@ -50,8 +50,10 @@ struct IntegralTypes {
|
||||
int8_t e;
|
||||
};
|
||||
|
||||
using InverseReader = bitsery::AdapterReader<InputAdapter, InverseEndiannessConfig>;
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
|
||||
TEST(DataEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
//fill initial values
|
||||
IntegralTypes src{};
|
||||
src.a = 0x1122334455667788;
|
||||
@@ -70,7 +72,7 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBytes<8>(src.a);
|
||||
bw.writeBytes<4>(src.b);
|
||||
bw.writeBytes<2>(src.c);
|
||||
@@ -78,7 +80,7 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
bw.writeBytes<1>(src.e);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
InverseReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<4>(res.b);
|
||||
@@ -93,25 +95,25 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
|
||||
EXPECT_THAT(res.e, Eq(resInv.e));
|
||||
}
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBuffer1ByteValuesThenEndiannessIsIgnored) {
|
||||
TEST(DataEndianness, WhenWrite1ByteValuesThenEndiannessIsIgnored) {
|
||||
//fill initial values
|
||||
constexpr size_t SIZE = 4;
|
||||
uint8_t src[SIZE] = {0xAA, 0xBB, 0xCC, 0xDD};
|
||||
uint8_t res[SIZE] = {};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<1>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
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
|
||||
EXPECT_THAT(res, ContainerEq(src));
|
||||
}
|
||||
|
||||
TEST(BufferEndianness, WhenWriteBufferMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
TEST(DataEndianness, WhenWriteMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
//fill initial values
|
||||
constexpr size_t SIZE = 4;
|
||||
uint16_t src[SIZE] = {0xAA00, 0xBB11, 0xCC22, 0xDD33};
|
||||
@@ -119,11 +121,11 @@ TEST(BufferEndianness, WhenWriteBufferMoreThan1ByteValuesThenValuesAreSwapped) {
|
||||
uint16_t res[SIZE] = {};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<2>(src, SIZE);
|
||||
bw.flush();
|
||||
//read from buffer using inverse endianness config
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
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
|
||||
@@ -143,15 +145,13 @@ struct IntegralUnsignedTypes {
|
||||
uint8_t d;
|
||||
};
|
||||
|
||||
TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
|
||||
//fill initial values
|
||||
static_assert(sizeof(bitsery::details::BufferContainerTraits<DefaultConfig::BufferType>::TValue) == 1, "currently only 1 byte size, value size is supported");
|
||||
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;
|
||||
@@ -160,19 +160,21 @@ TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedB
|
||||
constexpr size_t dBITS = getBits(src.d) + 2;
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
bitsery::BasicBufferWriter<DefaultConfig> bw{buf};
|
||||
bw.writeBits(src.a, aBITS);
|
||||
bw.writeBits(src.b, bBITS);
|
||||
bw.writeBits(src.c, cBITS);
|
||||
bw.writeBits(src.d, dBITS);
|
||||
bw.flush();
|
||||
Writer bw{buf};
|
||||
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
|
||||
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
|
||||
InverseReader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<InverseReader> bpr{br};
|
||||
IntegralUnsignedTypes res{};
|
||||
br.readBits(res.a, aBITS);
|
||||
br.readBits(res.b, bBITS);
|
||||
br.readBits(res.c, cBITS);
|
||||
br.readBits(res.d, dBITS);
|
||||
bpr.readBits(res.a, aBITS);
|
||||
bpr.readBits(res.b, bBITS);
|
||||
bpr.readBits(res.c, cBITS);
|
||||
bpr.readBits(res.d, dBITS);
|
||||
//check results
|
||||
EXPECT_THAT(res.a, Eq(src.a));
|
||||
EXPECT_THAT(res.b, Eq(src.b));
|
||||
360
tests/data_operations.cpp
Normal file
360
tests/data_operations.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/value_range.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
|
||||
using AdapterBitPackingWriter = bitsery::AdapterWriterBitPackingWrapper<Writer>;
|
||||
using AdapterBitPackingReader = bitsery::AdapterReaderBitPackingWrapper<Reader>;
|
||||
|
||||
|
||||
struct IntegralUnsignedTypes {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint8_t c;
|
||||
uint8_t d;
|
||||
uint64_t e;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr size_t getBits(T v) {
|
||||
return bitsery::details::calcRequiredBits<T>({}, v);
|
||||
};
|
||||
|
||||
// *** bits operations
|
||||
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBitsMaxTypeValues) {
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
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.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint64_t v64{};
|
||||
uint32_t v32{};
|
||||
uint16_t v16{};
|
||||
uint8_t v8{};
|
||||
bpr.readBits(v64, 64);
|
||||
bpr.readBits(v32, 32);
|
||||
bpr.readBits(v16, 16);
|
||||
bpr.readBits(v8, 8);
|
||||
|
||||
EXPECT_THAT(v64, Eq(std::numeric_limits<uint64_t>::max()));
|
||||
EXPECT_THAT(v32, Eq(std::numeric_limits<uint32_t>::max()));
|
||||
EXPECT_THAT(v16, Eq(std::numeric_limits<uint16_t>::max()));
|
||||
EXPECT_THAT(v8, Eq(std::numeric_limits<uint8_t>::max()));
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBits) {
|
||||
//setup data
|
||||
constexpr IntegralUnsignedTypes data{
|
||||
485454,//bits 19
|
||||
45978,//bits 16
|
||||
0,//bits 1
|
||||
36,//bits 6
|
||||
479845648946//bits 39
|
||||
};
|
||||
|
||||
constexpr size_t aBITS = getBits(data.a) + 2;
|
||||
constexpr size_t bBITS = getBits(data.b) + 0;
|
||||
constexpr size_t cBITS = getBits(data.c) + 2;
|
||||
constexpr size_t dBITS = getBits(data.d) + 1;
|
||||
constexpr size_t eBITS = getBits(data.e) + 8;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(data.a, aBITS);
|
||||
bpw.writeBits(data.b, bBITS);
|
||||
bpw.writeBits(data.c, cBITS);
|
||||
bpw.writeBits(data.d, dBITS);
|
||||
bpw.writeBits(data.e, eBITS);
|
||||
bpw.flush();
|
||||
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}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
|
||||
IntegralUnsignedTypes res{};
|
||||
|
||||
bpr.readBits(res.a, aBITS);
|
||||
bpr.readBits(res.b, bBITS);
|
||||
bpr.readBits(res.c, cBITS);
|
||||
bpr.readBits(res.d, dBITS);
|
||||
bpr.readBits(res.e, eBITS);
|
||||
|
||||
EXPECT_THAT(res.a, Eq(data.a));
|
||||
EXPECT_THAT(res.b, Eq(data.b));
|
||||
EXPECT_THAT(res.c, Eq(data.c));
|
||||
EXPECT_THAT(res.d, Eq(data.d));
|
||||
EXPECT_THAT(res.e, Eq(data.e));
|
||||
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, WrittenSizeIsCountedPerByteNotPerBit) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(7u,3);
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint16_t tmp;
|
||||
bpr.readBits(tmp,4);
|
||||
bpr.readBits(tmp,2);
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//part of next byte
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
bpr1.readBits(tmp,7);
|
||||
EXPECT_THAT(bpr1.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
|
||||
//bigger than byte
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
bpr2.readBits(tmp,9);
|
||||
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::DataOverflow));//false
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
bpw.writeBits(3u, 2);
|
||||
//3 calls to align after 1st data
|
||||
bpw.align();
|
||||
bpw.align();
|
||||
bpw.align();
|
||||
bpw.writeBits(7u, 3);
|
||||
//1 call to align after 2nd data
|
||||
bpw.align();
|
||||
bpw.writeBits(15u, 4);
|
||||
bpw.flush();
|
||||
|
||||
unsigned char tmp;
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
bpr.readBits(tmp,2);
|
||||
EXPECT_THAT(tmp, Eq(3u));
|
||||
bpr.align();
|
||||
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.error(), Eq(bitsery::ReaderError::NoError));
|
||||
|
||||
bpr.readBits(tmp,4);
|
||||
EXPECT_THAT(tmp, Eq(15u));
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::NoError));
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, AlignWritesZerosBits) {
|
||||
//setup data
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf;
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
|
||||
//write 2 bits and align
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(1));
|
||||
unsigned char tmp;
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
AdapterBitPackingReader bpr1{br1};
|
||||
bpr1.readBits(tmp,2);
|
||||
//read aligned bits
|
||||
bpr1.readBits(tmp,6);
|
||||
EXPECT_THAT(tmp, Eq(0));
|
||||
|
||||
Reader br2{InputAdapter{buf.begin(), writtenSize}};
|
||||
AdapterBitPackingReader bpr2{br2};
|
||||
//read 2 bits
|
||||
bpr2.readBits(tmp,2);
|
||||
bpr2.align();
|
||||
EXPECT_THAT(bpr2.error(), Eq(bitsery::ReaderError::NoError));
|
||||
}
|
||||
|
||||
|
||||
// *** bytes operations
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
uint32_t b;
|
||||
int16_t c;
|
||||
uint8_t d;
|
||||
int8_t e;
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(DataBitsAndBytesOperations, WriteAndReadBytes) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.a = -4894541654564;
|
||||
data.b = 94545646;
|
||||
data.c = -8778;
|
||||
data.d = 200;
|
||||
data.e = -98;
|
||||
data.f[0] = 43;
|
||||
data.f[1] = -45;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.writeBytes<8>(data.a);
|
||||
bw.writeBytes<1>(data.e);
|
||||
bw.writeBuffer<1>(data.f, 2);
|
||||
bw.flush();
|
||||
auto writtenSize = bw.writtenBytesCount();
|
||||
|
||||
EXPECT_THAT(writtenSize, Eq(18));
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), writtenSize}};
|
||||
IntegralTypes res{};
|
||||
br.readBytes<4>(res.b);
|
||||
br.readBytes<2>(res.c);
|
||||
br.readBytes<1>(res.d);
|
||||
br.readBytes<8>(res.a);
|
||||
br.readBytes<1>(res.e);
|
||||
br.readBuffer<1>(res.f, 2);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
//assert results
|
||||
|
||||
EXPECT_THAT(data.a, Eq(res.a));
|
||||
EXPECT_THAT(data.b, Eq(res.b));
|
||||
EXPECT_THAT(data.c, Eq(res.c));
|
||||
EXPECT_THAT(data.d, Eq(res.d));
|
||||
EXPECT_THAT(data.e, Eq(res.e));
|
||||
EXPECT_THAT(data.f, ContainerEq(res.f));
|
||||
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, ReadWriteFncCanAcceptSignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
bw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int16_t dst[DATA_SIZE]{};
|
||||
br1.readBuffer<2>(dst, DATA_SIZE);
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, ReadWriteCanWorkOnUnalignedData) {
|
||||
//setup data
|
||||
constexpr size_t DATA_SIZE = 3;
|
||||
int16_t src[DATA_SIZE] {54,-4877,30067};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
bpw.writeBits(15u, 4);
|
||||
bpw.writeBuffer<2>(src, DATA_SIZE);
|
||||
bpw.writeBits(12u, 4);
|
||||
bpw.flush();
|
||||
auto writtenSize = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, Eq(sizeof(src) + 1));
|
||||
|
||||
//read from buffer
|
||||
Reader br1{InputAdapter{buf.begin(), writtenSize}};
|
||||
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.error(), Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_THAT(dst, ContainerEq(src));
|
||||
bpr1.readBits(tmp, 4);
|
||||
EXPECT_THAT(tmp, Eq(12));
|
||||
}
|
||||
|
||||
TEST(DataBitsAndBytesOperations, RegressionTestReadBytesAfterReadBitsWithLotsOfZeroBits) {
|
||||
//setup data
|
||||
int16_t data[2]{0x0000, 0x7FFF};
|
||||
int16_t res[2]{};
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
Writer bw{buf};
|
||||
AdapterBitPackingWriter bpw{bw};
|
||||
bpw.writeBits(2u, 2);
|
||||
bpw.writeBytes<2>(data[0]);
|
||||
bpw.writeBytes<2>(data[1]);
|
||||
bpw.align();
|
||||
bpw.flush();
|
||||
|
||||
//read from buffer
|
||||
Reader br{InputAdapter{buf.begin(), bpw.writtenBytesCount()}};
|
||||
AdapterBitPackingReader bpr{br};
|
||||
uint8_t tmp{};
|
||||
bpr.readBits(tmp, 2);
|
||||
EXPECT_THAT(tmp, Eq(2));
|
||||
bpr.readBytes<2>(res[0]);
|
||||
bpr.readBytes<2>(res[1]);
|
||||
bpr.align();
|
||||
EXPECT_THAT(res[0], Eq(data[0]));
|
||||
EXPECT_THAT(res[1], Eq(data[1]));
|
||||
}
|
||||
|
||||
@@ -22,15 +22,11 @@
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <bitsery/buffer_writer.h>
|
||||
#include <bitsery/buffer_reader.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <list>
|
||||
#include <bitset>
|
||||
|
||||
using testing::Eq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
|
||||
struct IntegralTypes {
|
||||
int64_t a;
|
||||
@@ -41,48 +37,48 @@ struct IntegralTypes {
|
||||
int8_t f[2];
|
||||
};
|
||||
|
||||
TEST(BufferReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
TEST(DataReading, WhenReadingMoreThanAvailableThenEmptyBufferError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
TEST(BufferReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
TEST(DataReading, WhenErrorOccursThenAllOtherOperationsFailsForSameError) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
br.readBytes<1>(a);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
}
|
||||
|
||||
|
||||
TEST(BufferReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
TEST(DataReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors) {
|
||||
//setup data
|
||||
IntegralTypes data;
|
||||
data.b = 94545646;
|
||||
@@ -91,66 +87,67 @@ TEST(BufferReading, ReadIsCompletedSuccessfullyWhenAllBytesAreReadWithoutErrors)
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<4>(data.b);
|
||||
bw.writeBytes<2>(data.c);
|
||||
bw.writeBytes<1>(data.d);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
IntegralTypes res;
|
||||
br.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::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::BufferReaderError::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::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br.isCompletedSuccessfully(), Eq(false));
|
||||
|
||||
BufferReader br1{bw.getWrittenRange()};
|
||||
Reader br1{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br1.readBytes<4>(res.b);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::NoError));
|
||||
br1.readBytes<2>(res.c);
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::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::BufferReaderError::BUFFER_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::BufferReaderError::BUFFER_OVERFLOW));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
EXPECT_THAT(br1.isCompletedSuccessfully(), Eq(false));
|
||||
}
|
||||
|
||||
TEST(BufferReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
TEST(DataReading, WhenReaderHasErrorsAllOperationsReadsReturnZero) {
|
||||
//setup data
|
||||
uint8_t a = 111;
|
||||
|
||||
//create and write to buffer
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.writeBytes<1>(a);
|
||||
bw.flush();
|
||||
//read from buffer
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
int32_t c;
|
||||
br.readBytes<4>(c);
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
bpr.readBytes<4>(c);
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::DataOverflow));
|
||||
|
||||
int16_t r1= {-645};
|
||||
uint32_t r2[2] = {54898,87854};
|
||||
uint8_t r3 = 0xFF;
|
||||
|
||||
br.readBytes<2>(r1);
|
||||
br.readBuffer<4>(r2, 2);
|
||||
br.readBits(r3, 7);
|
||||
bpr.readBytes<2>(r1);
|
||||
bpr.readBuffer<4>(r2, 2);
|
||||
bpr.readBits(r3, 7);
|
||||
EXPECT_THAT(r1, Eq(0));
|
||||
EXPECT_THAT(r2[0], Eq(0u));
|
||||
EXPECT_THAT(r2[1], Eq(0u));
|
||||
@@ -22,51 +22,53 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using testing::Eq;
|
||||
using bitsery::BufferWriter;
|
||||
using bitsery::BufferReader;
|
||||
using Buffer = bitsery::DefaultConfig::BufferType;
|
||||
using SessionsEnabledWriter = bitsery::AdapterWriter<OutputAdapter, SessionsEnabledConfig>;
|
||||
using SessionsEnabledReader = bitsery::AdapterReader<InputAdapter, SessionsEnabledConfig>;
|
||||
|
||||
TEST(BufferReadingErrors, WhenContainerOrTextSizeIsMoreThanMaxThenInvalidBufferDataError) {
|
||||
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::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingBoolByteReadsMoreThanOneThenInvalidBufferDataErrorAndResultIsFalse) {
|
||||
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();
|
||||
des.boolByte(res);
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(true));
|
||||
des.boolByte(res);
|
||||
des.boolValue(res);
|
||||
EXPECT_THAT(res, Eq(false));
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(ctx.br->error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingAlignHasNonZerosThenInvalidBufferDataError) {
|
||||
TEST(DataReadingErrors, WhenReadingAlignHasNonZerosThenInvalidDataError) {
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
Writer bw{buf};
|
||||
uint8_t tmp{0xFF};
|
||||
bw.writeBytes<1>(tmp);
|
||||
bw.flush();
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
|
||||
br.readBits(tmp,3);
|
||||
br.align();
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
Reader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
bitsery::AdapterReaderBitPackingWrapper<Reader> bpr{br};
|
||||
|
||||
bpr.readBits(tmp,3);
|
||||
bpr.align();
|
||||
EXPECT_THAT(bpr.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBufferError) {
|
||||
TEST(DataReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidDataError) {
|
||||
uint8_t tmp{0xFF};
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
bw.beginSession();
|
||||
bw.writeBytes<1>(tmp);
|
||||
@@ -74,7 +76,7 @@ TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBuffe
|
||||
bw.endSession();
|
||||
}
|
||||
bw.flush();
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
br.beginSession();
|
||||
br.readBytes<1>(tmp);
|
||||
@@ -83,37 +85,37 @@ TEST(BufferReadingErrors, WhenReadingNewSessionInMiddleOfOldDataThenInvalidBuffe
|
||||
br.endSession();
|
||||
br.endSession();
|
||||
}
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
|
||||
TEST(BufferReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidBufferData) {
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhenNotEnoughDataThenInvalidData) {
|
||||
uint8_t tmp1{0xFF};
|
||||
Buffer buf1{};
|
||||
BufferWriter bw1{buf1};
|
||||
SessionsEnabledWriter bw1{buf1};
|
||||
bw1.writeBytes<1>(tmp1);
|
||||
bw1.flush();
|
||||
BufferReader br1{bw1.getWrittenRange()};
|
||||
SessionsEnabledReader br1{InputAdapter{buf1.begin(), bw1.writtenBytesCount()}};
|
||||
br1.beginSession();
|
||||
EXPECT_THAT(br1.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br1.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
|
||||
Buffer buf2{};
|
||||
BufferWriter bw2{buf2};
|
||||
SessionsEnabledWriter bw2{buf2};
|
||||
uint16_t tmp2{0x8000};
|
||||
bw2.writeBytes<2>(tmp2);
|
||||
bw2.flush();
|
||||
BufferReader br2{bw2.getWrittenRange()};
|
||||
SessionsEnabledReader br2{InputAdapter{buf2.begin(), bw2.writtenBytesCount()}};
|
||||
br2.beginSession();
|
||||
EXPECT_THAT(br2.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br2.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
|
||||
TEST(BufferReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidBufferData) {
|
||||
TEST(DataReadingErrors, WhenInitializingSessionsWhereSessionsDataOffsetIsCorruptedThenInvalidData) {
|
||||
Buffer buf{};
|
||||
BufferWriter bw{buf};
|
||||
SessionsEnabledWriter bw{buf};
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<1>(uint8_t{1});
|
||||
bw.writeBytes<2>(uint16_t{10});
|
||||
BufferReader br{bw.getWrittenRange()};
|
||||
SessionsEnabledReader br{InputAdapter{buf.begin(), bw.writtenBytesCount()}};
|
||||
br.beginSession();
|
||||
EXPECT_THAT(br.getError(), Eq(bitsery::BufferReaderError::INVALID_BUFFER_DATA));
|
||||
EXPECT_THAT(br.error(), Eq(bitsery::ReaderError::InvalidData));
|
||||
}
|
||||
110
tests/data_writing.cpp
Normal file
110
tests/data_writing.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/details/serialization_common.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::ContainerEq;
|
||||
using bitsery::EndiannessType;
|
||||
|
||||
template <typename BufType>
|
||||
class DataWriting:public testing::Test {
|
||||
public:
|
||||
using TWriter = bitsery::AdapterWriter<bitsery::OutputBufferAdapter<BufType>, bitsery::DefaultConfig>;
|
||||
using TBuffer = BufType;
|
||||
};
|
||||
|
||||
using NonFixedContainer = std::vector<uint8_t>;
|
||||
using FixedContainer = std::array<uint8_t, 100>;
|
||||
|
||||
using ContainerTypes = ::testing::Types<FixedContainer,NonFixedContainer>;
|
||||
|
||||
TYPED_TEST_CASE(DataWriting, ContainerTypes);
|
||||
|
||||
static constexpr size_t DATA_SIZE = 14u;
|
||||
|
||||
template <typename BW>
|
||||
void writeData(BW& bw) {
|
||||
uint16_t tmp1{45}, tmp2{6543}, tmp3{46533};
|
||||
uint32_t tmp4{8979445}, tmp5{7987564};
|
||||
bw.template writeBytes<2>(tmp1);
|
||||
bw.template writeBytes<2>(tmp2);
|
||||
bw.template writeBytes<2>(tmp3);
|
||||
bw.template writeBytes<4>(tmp4);
|
||||
bw.template writeBytes<4>(tmp5);
|
||||
}
|
||||
|
||||
TYPED_TEST(DataWriting, GetWrittenBytesCountReturnsActualBytesWritten) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
writeData(bw);
|
||||
bw.flush();
|
||||
auto writtenSize = bw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize, DATA_SIZE);
|
||||
EXPECT_THAT(buf.size(), ::testing::Ge(DATA_SIZE));
|
||||
}
|
||||
|
||||
TYPED_TEST(DataWriting, WhenWritingBitsThenMustFlushWriter) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
bpw.flush();
|
||||
auto writtenSize2 = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(0));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(DataWriting, WhenDataAlignedThenFlushHasNoEffect) {
|
||||
using TWriter = typename TestFixture::TWriter;
|
||||
using TBuffer = typename TestFixture::TBuffer;
|
||||
TBuffer buf{};
|
||||
TWriter bw{buf};
|
||||
bitsery::AdapterWriterBitPackingWrapper<TWriter> bpw{bw};
|
||||
bpw.writeBits(3u, 2);
|
||||
bpw.align();
|
||||
auto writtenSize1 = bpw.writtenBytesCount();
|
||||
bpw.flush();
|
||||
auto writtenSize2 = bpw.writtenBytesCount();
|
||||
EXPECT_THAT(writtenSize1, Eq(1));
|
||||
EXPECT_THAT(writtenSize2, Eq(1));
|
||||
|
||||
}
|
||||
|
||||
TEST(DataWritingNonFixedBufferContainer, ContainerIsAlwaysResizedToCapacity) {
|
||||
NonFixedContainer buf{};
|
||||
bitsery::AdapterWriter<bitsery::OutputBufferAdapter<NonFixedContainer>, bitsery::DefaultConfig> bw{buf};
|
||||
for (auto i = 0; i < 5; ++i) {
|
||||
uint32_t tmp{};
|
||||
bw.writeBytes<4>(tmp);
|
||||
bw.writeBytes<4>(tmp);
|
||||
EXPECT_TRUE(buf.size() == buf.capacity());
|
||||
}
|
||||
}
|
||||
386
tests/flexible_syntax.cpp
Normal file
386
tests/flexible_syntax.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/flexible.h>
|
||||
|
||||
#include <bitsery/flexible/string.h>
|
||||
#include <bitsery/flexible/array.h>
|
||||
#include <bitsery/flexible/vector.h>
|
||||
#include <bitsery/flexible/list.h>
|
||||
#include <bitsery/flexible/forward_list.h>
|
||||
#include <bitsery/flexible/deque.h>
|
||||
#include <bitsery/flexible/queue.h>
|
||||
#include <bitsery/flexible/stack.h>
|
||||
#include <bitsery/flexible/map.h>
|
||||
#include <bitsery/flexible/unordered_map.h>
|
||||
#include <bitsery/flexible/set.h>
|
||||
#include <bitsery/flexible/unordered_set.h>
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
TEST(FlexibleSyntax, FundamentalTypesAndBool) {
|
||||
int ti = 8745;
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double_t td = -454184.48445;
|
||||
bool tb=true;
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(ti,te,tf,td,tb);
|
||||
|
||||
//result
|
||||
int ri{};
|
||||
MyEnumClass re{};
|
||||
float rf{};
|
||||
double_t rd{};
|
||||
bool rb{};
|
||||
ctx.createDeserializer().archive(ri,re,rf,rd,rb);
|
||||
|
||||
//test
|
||||
EXPECT_THAT(ri, Eq(ti));
|
||||
EXPECT_THAT(re, Eq(te));
|
||||
EXPECT_THAT(rf, Eq(tf));
|
||||
EXPECT_THAT(rd, Eq(td));
|
||||
EXPECT_THAT(rb, Eq(tb));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, UseObjectFncInsteadOfValueN) {
|
||||
int ti = 8745;
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double_t td = -454184.48445;
|
||||
bool tb=true;
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(ti);
|
||||
ser.object(te);
|
||||
ser.object(tf);
|
||||
ser.object(td);
|
||||
ser.object(tb);
|
||||
|
||||
//result
|
||||
int ri{};
|
||||
MyEnumClass re{};
|
||||
float rf{};
|
||||
double_t rd{};
|
||||
bool rb{};
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(ri);
|
||||
des.object(re);
|
||||
des.object(rf);
|
||||
des.object(rd);
|
||||
des.object(rb);
|
||||
|
||||
//test
|
||||
EXPECT_THAT(ri, Eq(ti));
|
||||
EXPECT_THAT(re, Eq(te));
|
||||
EXPECT_THAT(rf, Eq(tf));
|
||||
EXPECT_THAT(rd, Eq(td));
|
||||
EXPECT_THAT(rb, Eq(tb));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, MixDifferentSyntax) {
|
||||
int ti = 8745;
|
||||
MyEnumClass te = MyEnumClass::E4;
|
||||
float tf = 485.042f;
|
||||
double_t td = -454184.48445;
|
||||
bool tb=true;
|
||||
SerializationContext ctx;
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.value<sizeof(ti)>(ti);
|
||||
ser.archive(te, tf, td);
|
||||
ser.object(tb);
|
||||
|
||||
//result
|
||||
int ri{};
|
||||
MyEnumClass re{};
|
||||
float rf{};
|
||||
double_t rd{};
|
||||
bool rb{};
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.archive(ri, re, rf);
|
||||
des.value8b(rd);
|
||||
des.object(rb);
|
||||
|
||||
//test
|
||||
EXPECT_THAT(ri, Eq(ti));
|
||||
EXPECT_THAT(re, Eq(te));
|
||||
EXPECT_THAT(rf, Eq(tf));
|
||||
EXPECT_THAT(rd, Eq(td));
|
||||
EXPECT_THAT(rb, Eq(tb));
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
T procArchive(const T& testData) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(testData);
|
||||
T res;
|
||||
ctx.createDeserializer().archive(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T procArchiveWithMaxSize(const T& testData) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::maxSize(testData, 100));
|
||||
T res;
|
||||
ctx.createDeserializer().archive(bitsery::maxSize(res, 100));
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, CStyleArrayForValueTypesAsContainer) {
|
||||
const int t1[3]{8748,-484,45};
|
||||
int r1[3]{0,0,0};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::asContainer(t1));
|
||||
ctx.createDeserializer().archive(bitsery::asContainer(r1));
|
||||
|
||||
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, CStyleArrayForIntegralTypesAsText) {
|
||||
const char t1[3]{"hi"};
|
||||
char r1[3]{0,0,0};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(bitsery::asText(t1));
|
||||
ctx.createDeserializer().archive(bitsery::asText(r1));
|
||||
|
||||
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, CStyleArray) {
|
||||
const MyEnumClass t1[3]{MyEnumClass::E1, MyEnumClass::E4, MyEnumClass::E2};
|
||||
MyEnumClass r1[3]{};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().archive(t1);
|
||||
ctx.createDeserializer().archive(r1);
|
||||
|
||||
EXPECT_THAT(r1, ::testing::ContainerEq(t1));
|
||||
}
|
||||
|
||||
|
||||
TEST(FlexibleSyntax, StdString) {
|
||||
std::string t1{"my nice string"};
|
||||
std::string t2{};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdArray) {
|
||||
std::array<int, 3> t1{8748,-484,45};
|
||||
std::array<int, 0> t2{};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdVector) {
|
||||
std::vector<int> t1{8748,-484,45};
|
||||
std::vector<float> t2{5.f,0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdList) {
|
||||
std::list<int> t1{8748,-484,45};
|
||||
std::list<float> t2{5.f,0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdForwardList) {
|
||||
std::forward_list<int> t1{8748,-484,45};
|
||||
std::forward_list<float> t2{5.f,0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdDeque) {
|
||||
std::deque<int> t1{8748,-484,45};
|
||||
std::deque<float> t2{5.f,0.198f};
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchive(t2), Eq(t2));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t2), Eq(t2));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdQueue) {
|
||||
std::queue<std::string> t1;
|
||||
t1.push("first");
|
||||
t1.push("second string");
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdPriorityQueue) {
|
||||
std::priority_queue<std::string> t1;
|
||||
t1.push("first");
|
||||
t1.push("second string");
|
||||
t1.push("third");
|
||||
t1.push("fourth");
|
||||
auto r1 = procArchive(t1);
|
||||
//we cannot compare priority queue directly
|
||||
|
||||
EXPECT_THAT(r1.size(), Eq(t1.size()));
|
||||
for (auto i = 0u; i < r1.size(); ++i) {
|
||||
EXPECT_THAT(r1.top(), Eq(t1.top()));
|
||||
r1.pop();
|
||||
t1.pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdStack) {
|
||||
std::stack<std::string> t1;
|
||||
t1.push("first");
|
||||
t1.push("second string");
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedMap) {
|
||||
std::unordered_map<int, int> t1;
|
||||
t1.emplace(3423,624);
|
||||
t1.emplace(-5484,-845);
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedMultiMap) {
|
||||
std::unordered_multimap<std::string, int> t1;
|
||||
t1.emplace("one",624);
|
||||
t1.emplace("two",-845);
|
||||
t1.emplace("one",897);
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdMap) {
|
||||
std::map<int, int> t1;
|
||||
t1.emplace(3423,624);
|
||||
t1.emplace(-5484,-845);
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdMultiMap) {
|
||||
std::multimap<std::string, int> t1;
|
||||
t1.emplace("one",624);
|
||||
t1.emplace("two",-845);
|
||||
t1.emplace("one",897);
|
||||
|
||||
auto res = procArchive(t1);
|
||||
//same key values is not ordered, and operator == compares each element at same position
|
||||
//so we need to compare our selves
|
||||
EXPECT_THAT(res.size(), Eq(3));
|
||||
for (auto it = t1.begin(); it != t1.end();) {
|
||||
const auto lr = t1.equal_range(it->first);
|
||||
const auto rr = res.equal_range(it->first);
|
||||
EXPECT_TRUE(std::distance(lr.first, lr.second) == std::distance(rr.first, rr.second));
|
||||
EXPECT_TRUE(std::is_permutation(lr.first, lr.second, rr.first));
|
||||
it = lr.second;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedSet) {
|
||||
std::unordered_set<std::string> t1;
|
||||
t1.emplace("one");
|
||||
t1.emplace("two");
|
||||
t1.emplace("three");
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdUnorderedMultiSet) {
|
||||
std::unordered_multiset<std::string> t1;
|
||||
t1.emplace("one");
|
||||
t1.emplace("two");
|
||||
t1.emplace("three");
|
||||
t1.emplace("one");
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdSet) {
|
||||
std::set<std::string> t1;
|
||||
t1.emplace("one");
|
||||
t1.emplace("two");
|
||||
t1.emplace("three");
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
TEST(FlexibleSyntax, StdMultiSet) {
|
||||
std::multiset<std::string> t1;
|
||||
t1.emplace("one");
|
||||
t1.emplace("two");
|
||||
t1.emplace("three");
|
||||
t1.emplace("one");
|
||||
t1.emplace("two");
|
||||
|
||||
EXPECT_TRUE(procArchive(t1) == t1);
|
||||
EXPECT_TRUE(procArchiveWithMaxSize(t1) == t1);
|
||||
}
|
||||
|
||||
|
||||
TEST(FlexibleSyntax, NestedTypes) {
|
||||
std::unordered_map<std::string, std::vector<std::string>> t1;
|
||||
t1.emplace("my key", std::vector<std::string>{"very", "nice", "string"});
|
||||
t1.emplace("other key", std::vector<std::string>{"just a string"});
|
||||
|
||||
EXPECT_THAT(procArchive(t1), Eq(t1));
|
||||
EXPECT_THAT(procArchiveWithMaxSize(t1), Eq(t1));
|
||||
}
|
||||
@@ -26,19 +26,28 @@
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
using Serializer = bitsery::BasicSerializer<bitsery::AdapterWriterBitPackingWrapper<Writer>>;
|
||||
|
||||
using Deserializer = bitsery::BasicDeserializer<bitsery::AdapterReaderBitPackingWrapper<Reader>>;
|
||||
|
||||
|
||||
TEST(SerializeBooleans, BoolAsBit) {
|
||||
|
||||
SerializationContext ctx;
|
||||
bool t1{true};
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.boolBit(t1);
|
||||
ser.boolBit(t2);
|
||||
auto des = ctx.createDeserializer();
|
||||
des.boolBit(res1);
|
||||
des.boolBit(res2);
|
||||
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& sbp) {
|
||||
sbp.boolValue(res1);
|
||||
sbp.boolValue(res2);
|
||||
});
|
||||
|
||||
EXPECT_THAT(res1, Eq(t1));
|
||||
EXPECT_THAT(res2, Eq(t2));
|
||||
@@ -51,12 +60,12 @@ TEST(SerializeBooleans, BoolAsByte) {
|
||||
bool t2{false};
|
||||
bool res1;
|
||||
bool res2;
|
||||
auto ser = ctx.createSerializer();
|
||||
ser.boolByte(t1);
|
||||
ser.boolByte(t2);
|
||||
auto des = ctx.createDeserializer();
|
||||
des.boolByte(res1);
|
||||
des.boolByte(res2);
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.boolValue(t1);
|
||||
ser.boolValue(t2);
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.boolValue(res1);
|
||||
des.boolValue(res2);
|
||||
|
||||
EXPECT_THAT(res1, Eq(t1));
|
||||
EXPECT_THAT(res2, Eq(t2));
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <bitsery/traits/list.h>
|
||||
#include <bitsery/traits/deque.h>
|
||||
#include <bitsery/traits/forward_list.h>
|
||||
|
||||
using testing::ContainerEq;
|
||||
using testing::Eq;
|
||||
@@ -78,13 +80,15 @@ public:
|
||||
TContainer res{};
|
||||
|
||||
size_t getExpectedBufSize(const SerializationContext &ctx) const {
|
||||
return ctx.containerSizeSerializedBytesCount(src.size()) + src.size() * sizeof(TValue);
|
||||
auto size = bitsery::traits::ContainerTraits<TContainer>::size(src);
|
||||
return ctx.containerSizeSerializedBytesCount(size) + size * sizeof(TValue);
|
||||
}
|
||||
};
|
||||
//std::forward_list is not supported, because it doesn't have size() method
|
||||
using SequenceContainersWithArthmeticTypes = ::testing::Types<
|
||||
std::vector<int>,
|
||||
std::list<float>,
|
||||
std::forward_list<int>,
|
||||
std::deque<unsigned short>>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeContainerDynamicSizeArthmeticTypes, SequenceContainersWithArthmeticTypes);
|
||||
@@ -104,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
|
||||
@@ -229,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);
|
||||
|
||||
@@ -24,29 +24,39 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <bitsery/traits/list.h>
|
||||
|
||||
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.createSerializer().ext4b(v, Entropy<int32_t[3]>{values});
|
||||
ctx.createDeserializer().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.createSerializer().ext4b(v, Entropy<int32_t[3]>{values});
|
||||
auto des = ctx1.createDeserializer();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -55,8 +65,12 @@ TEST(SerializeExtensionEntropy, WhenNoEntropyEncodedThenWriteZeroBitsAndValueOrO
|
||||
int16_t res;
|
||||
std::initializer_list<int> values{485,4849,89};
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().ext2b(v, Entropy<std::initializer_list<int>>{values});
|
||||
ctx.createDeserializer().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));
|
||||
@@ -71,9 +85,12 @@ TEST(SerializeExtensionEntropy, CustomTypeEntropyEncoded) {
|
||||
MyStruct1{12, 10}, MyStruct1{485, 454},
|
||||
MyStruct1{4849, 89}, MyStruct1{0, 1}};
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().ext(v, Entropy<MyStruct1[N]>{values});
|
||||
ctx.createDeserializer().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));
|
||||
}
|
||||
@@ -86,15 +103,18 @@ TEST(SerializeExtensionEntropy, CustomTypeNotEntropyEncoded) {
|
||||
MyStruct1{12,10}, MyStruct1{485, 454},
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
SerializationContext ctx;
|
||||
|
||||
ctx.createSerializer().ext(v, Entropy<std::initializer_list<MyStruct1>>{values});
|
||||
ctx.createDeserializer().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));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncoded) {
|
||||
TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithNoAlignBeforeData) {
|
||||
MyStruct1 v = {8945,4456};
|
||||
MyStruct1 res;
|
||||
constexpr size_t N = 4;
|
||||
@@ -104,29 +124,62 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncoded) {
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
|
||||
auto rangeForValue = bitsery::ext::ValueRange<int>{0, 10000};
|
||||
auto rangeForIndex = bitsery::ext::ValueRange<size_t>{0u, N+1};
|
||||
|
||||
SerializationContext ctx;
|
||||
auto ser = ctx.createSerializer();
|
||||
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& v) {
|
||||
ser.ext(v.i1, rangeForValue);
|
||||
ser.ext(v.i2, rangeForValue);
|
||||
};
|
||||
ser.ext(v, Entropy<std::vector<MyStruct1>>(values), serLambda);
|
||||
|
||||
auto des = ctx.createDeserializer();
|
||||
auto desLambda = [&des, &rangeForValue](MyStruct1& v) {
|
||||
des.ext(v.i1, rangeForValue);
|
||||
des.ext(v.i2, rangeForValue);
|
||||
};
|
||||
des.ext(res, Entropy<std::vector<MyStruct1>>(values), 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};
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq((rangeForIndex.getRequiredBits() + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1 ));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeData) {
|
||||
MyStruct1 v = {8945,4456};
|
||||
MyStruct1 res;
|
||||
|
||||
std::vector<MyStruct1> values{
|
||||
MyStruct1{12,10}, MyStruct1{485, 454},
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
|
||||
auto rangeForValue = bitsery::ext::ValueRange<int>{0, 10000};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().enableBitPacking([&v, &values, &rangeForValue](BPSer& ser){
|
||||
//lambdas differ only in capture clauses, it would make sense to use std::bind, but debugger crashes when it sees std::bind...
|
||||
auto serLambda = [&ser, &rangeForValue](MyStruct1& data) {
|
||||
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
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq((bitsForIndex + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1 ));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked) {
|
||||
MyStruct1 v = {4849,89};
|
||||
MyStruct1 res;
|
||||
@@ -135,8 +188,12 @@ TEST(SerializeExtensionEntropy, WhenEntropyEncodedThenCustomFunctionNotInvoked)
|
||||
MyStruct1{4849,89}, MyStruct1{0,1}};
|
||||
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer().ext(v, Entropy<std::list<MyStruct1>>{values}, [](MyStruct1& ) {});
|
||||
ctx.createDeserializer().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));
|
||||
|
||||
@@ -28,9 +28,6 @@ using namespace testing;
|
||||
|
||||
using bitsery::ext::Growable;
|
||||
|
||||
using Buffer = typename bitsery::DefaultConfig::BufferType;
|
||||
using DiffType = typename bitsery::details::BufferContainerTraits<Buffer>::TDifference;
|
||||
|
||||
struct DataV1 {
|
||||
int32_t v1;
|
||||
};
|
||||
@@ -48,7 +45,7 @@ struct DataV3 {
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
ctx.createSerializer().ext(int8_t{}, Growable{}, [] (int8_t& v) { });
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(0));
|
||||
ctx.bw->flush();
|
||||
@@ -56,18 +53,18 @@ TEST(SerializeExtensionGrowable, WriteSessionsDataAtBufferEndAfterFlush) {
|
||||
}
|
||||
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSessionsDataOffset) {
|
||||
SerializationContext ctx;
|
||||
TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd4BytesSessionsDataOffset) {
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
|
||||
|
||||
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
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(3 + DATA_SIZE));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1+4 + DATA_SIZE));
|
||||
|
||||
//read value back
|
||||
auto& br = *(ctx.br);
|
||||
@@ -78,16 +75,16 @@ TEST(SerializeExtensionGrowable, SessionDataConsistOfSessionsEndPosAnd2BytesSess
|
||||
bitsery::details::readSize(br, sessionEnd, 1000000u);
|
||||
EXPECT_THAT(sessionEnd, Eq(DATA_SIZE));
|
||||
//this is the the offset from the end of buffer where actual data ends
|
||||
uint16_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<2>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+2));//1byte for session info, 2 bytes for session offset variable
|
||||
auto range = ctx.bw->getWrittenRange();
|
||||
auto dSize = std::distance(range.begin(), std::next(range.end(), -sessionsOffset));
|
||||
uint32_t sessionsOffset{};//bufferEnd - sessionsOffset = dataEnd
|
||||
br.readBytes<4>(sessionsOffset);
|
||||
EXPECT_THAT(sessionsOffset, Eq(1+4));//1byte for session info, 4 bytes for session offset variable
|
||||
auto writtenSize = ctx.bw->writtenBytesCount();
|
||||
auto dSize = writtenSize - sessionsOffset;
|
||||
EXPECT_THAT(dSize, Eq(DATA_SIZE));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{19457,846, 498418};
|
||||
ctx.createSerializer();
|
||||
ctx.bw->beginSession();
|
||||
@@ -118,29 +115,8 @@ TEST(SerializeExtensionGrowable, WhenNestedSessionsThenStoreEachDepthAndSize) {
|
||||
EXPECT_THAT(sessionEnd[2], Eq(12));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, WhenSessionsDataIsMoreThan0x7FFFThenWrite4BytesForSessionsOffset) {
|
||||
SerializationContext ctx;
|
||||
ctx.createSerializer();
|
||||
//create more sessions that can fit in 2 bytes
|
||||
for (auto i = 0u; i < 0x8000; ++i) {
|
||||
ctx.bw->beginSession();
|
||||
ctx.bw->endSession();
|
||||
}
|
||||
ctx.createDeserializer();//to flush data and create buffer reader
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(0x8000+4));
|
||||
uint8_t tmp{};
|
||||
for (auto i = 0u; i < 0x8000; ++i) {
|
||||
ctx.br->beginSession();
|
||||
ctx.br->endSession();
|
||||
}
|
||||
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::NO_ERROR));
|
||||
ctx.br->readBytes<1>(tmp);
|
||||
EXPECT_THAT(ctx.br->getError(), Eq(bitsery::BufferReaderError::BUFFER_OVERFLOW));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -166,7 +142,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadSameVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -193,7 +169,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadNewerVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -221,7 +197,7 @@ TEST(SerializeExtensionGrowable, MultipleSessionsReadOlderVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -257,7 +233,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadSameVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -297,7 +273,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadOlderVersionData) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -353,7 +329,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData1) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV3 data{8454,987451,54};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
@@ -415,7 +391,7 @@ TEST(SerializeExtensionGrowable, MultipleNestedSessionsReadNewerVersionData2) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionGrowable, SessionsStartsAtEndOfSerialization) {
|
||||
SerializationContext ctx;
|
||||
BasicSerializationContext<SessionsEnabledConfig> ctx;
|
||||
DataV2 data{8454,987451};
|
||||
ctx.createSerializer();
|
||||
auto& bw = (*ctx.bw);
|
||||
|
||||
@@ -23,17 +23,15 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/container_map.h>
|
||||
#include <bitsery/ext/std_map.h>
|
||||
#include <bitsery/ext/entropy.h>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using ContainerMap = bitsery::ext::ContainerMap;
|
||||
using StdMap = bitsery::ext::StdMap;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
|
||||
|
||||
template<typename Container>
|
||||
Container createData() {
|
||||
return {};
|
||||
@@ -49,7 +47,7 @@ std::unordered_map<std::string, MyStruct1> createData<std::unordered_map<std::st
|
||||
}
|
||||
|
||||
template<>
|
||||
std::unordered_map<int32_t, float> createData<std::unordered_map<int32_t, float>>() {
|
||||
std::unordered_multimap<int32_t, float> createData<std::unordered_multimap<int32_t, float>>() {
|
||||
return {
|
||||
std::pair<int32_t , float>(545, 45.485f),
|
||||
std::pair<int32_t , float>(6748, -7891.5f),
|
||||
@@ -67,7 +65,7 @@ std::map<MyEnumClass, MyStruct1> createData<std::map<MyEnumClass, MyStruct1>>()
|
||||
}
|
||||
|
||||
template<>
|
||||
std::map<int32_t ,int64_t> createData<std::map<int32_t ,int64_t>>() {
|
||||
std::multimap<int32_t ,int64_t> createData<std::multimap<int32_t ,int64_t>>() {
|
||||
return {//these are optimized with range and entropy
|
||||
std::pair<int32_t, int64_t>(-45, -984196845ll),
|
||||
std::pair<int32_t, int64_t>(54, 1ll),
|
||||
@@ -76,7 +74,7 @@ std::map<int32_t ,int64_t> createData<std::map<int32_t ,int64_t>>() {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionContainerMap : public testing::Test {
|
||||
class SerializeExtensionStdMap : public testing::Test {
|
||||
public:
|
||||
using TContainer = T;
|
||||
|
||||
@@ -84,28 +82,28 @@ public:
|
||||
TContainer res{};
|
||||
};
|
||||
|
||||
using SerializeExtensionContainerMapTypes = ::testing::Types<
|
||||
using SerializeExtensionStdMapTypes = ::testing::Types<
|
||||
std::unordered_map<std::string, MyStruct1>,
|
||||
std::unordered_map<int32_t, float>,
|
||||
std::unordered_multimap<int32_t, float>,
|
||||
std::map<MyEnumClass , MyStruct1>,
|
||||
std::map<int32_t ,int64_t>
|
||||
std::multimap<int32_t ,int64_t>
|
||||
>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionContainerMap, SerializeExtensionContainerMapTypes);
|
||||
TYPED_TEST_CASE(SerializeExtensionStdMap, SerializeExtensionStdMapTypes);
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::unordered_map<std::string, MyStruct1>& o) {
|
||||
s.ext(o, ContainerMap{10}, [&s](std::string& key, MyStruct1& value) {
|
||||
s.ext(o, StdMap{10}, [&s](std::string& key, MyStruct1& value) {
|
||||
s.text1b(key, 100);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::unordered_map<int32_t, float>& o) {
|
||||
s.ext(o, ContainerMap{10}, [&s](int32_t& key, float& value) {
|
||||
void serialize(S& s, std::unordered_multimap<int32_t, float>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](int32_t& key, float& value) {
|
||||
s.value4b(key);
|
||||
s.value4b(value);
|
||||
});
|
||||
@@ -113,26 +111,27 @@ namespace bitsery {
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::map<MyEnumClass , MyStruct1>& o) {
|
||||
s.ext(o, ContainerMap{10}, [&s](MyEnumClass& key, MyStruct1& value) {
|
||||
s.ext(o, StdMap{10}, [&s](MyEnumClass& key, MyStruct1& value) {
|
||||
s.value4b(key);
|
||||
s.object(value);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::map<int32_t ,int64_t>& o) {
|
||||
s.ext(o, ContainerMap{10}, [&s](int32_t& key, int64_t& value) {
|
||||
int64_t values[3]{1ll, 2ll, 3ll};
|
||||
|
||||
s.ext(key, bitsery::ext::ValueRange<int32_t>{-100,100});
|
||||
s.ext8b(value, bitsery::ext::Entropy<int64_t[3]>{values});
|
||||
void serialize(S& s, std::multimap<int32_t ,int64_t>& o) {
|
||||
s.ext(o, StdMap{10}, [&s](int32_t& key, int64_t& value) {
|
||||
s.enableBitPacking([&key, &value](typename S::BPEnabledType& sbp) {
|
||||
int64_t values[3]{1ll, 2ll, 3ll};
|
||||
sbp.ext(key, bitsery::ext::ValueRange<int32_t>{-100,100});
|
||||
sbp.ext8b(value, bitsery::ext::Entropy<int64_t[3]>{values});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
TYPED_TEST(SerializeExtensionContainerMap, SerializeAndDeserializeEquals) {
|
||||
TYPED_TEST(SerializeExtensionStdMap, SerializeAndDeserializeEquals) {
|
||||
SerializationContext ctx1;
|
||||
ctx1.createSerializer().object(this->src);
|
||||
ctx1.createDeserializer().object(this->res);
|
||||
128
tests/serialization_ext_std_optional.cpp
Normal file
128
tests/serialization_ext_std_optional.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
|
||||
#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) {
|
||||
ctx.createSerializer().ext4b(v, StdOptional{});
|
||||
ctx.createDeserializer().ext4b(r, StdOptional{});
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdOptional, EmptyOptional) {
|
||||
std::optional<int32_t> t1{};
|
||||
std::optional<int32_t> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
|
||||
|
||||
r1 = 3;
|
||||
SerializationContext ctx2;
|
||||
test(ctx2,t1, r1);
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdOptional, OptionalHasValue) {
|
||||
std::optional<int32_t> t1{43};
|
||||
std::optional<int32_t> r1{52};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1 + sizeof(int)));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
r1 = std::optional<int>{};
|
||||
SerializationContext ctx2;
|
||||
test(ctx2,t1, r1);
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(1 + sizeof(int)));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
|
||||
ser.ext(t1, StdOptional(true), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(true), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(2));//1byte for index + 1byte for value
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
}
|
||||
|
||||
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;
|
||||
ctx.createSerializer().enableBitPacking([&t1, &range](BPSer& ser) {
|
||||
ser.ext(t1, StdOptional(false), [&ser, &range](int32_t& v) {
|
||||
ser.ext(v, range);
|
||||
});
|
||||
});
|
||||
ctx.createDeserializer().enableBitPacking([&r1, &range](BPDes& des) {
|
||||
des.ext(r1, StdOptional(false), [&des, &range](int32_t& v) {
|
||||
des.ext(v, range);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
EXPECT_THAT(range.getRequiredBits() + 1, ::testing::Lt(8));
|
||||
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
99
tests/serialization_ext_std_queue.cpp
Normal file
99
tests/serialization_ext_std_queue.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_queue.h>
|
||||
|
||||
using StdQueue = bitsery::ext::StdQueue;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
//inherit from queue so we could take underlying container, because priority queue doesn't have equal operator defined
|
||||
template <typename T, typename C>
|
||||
struct PriorityQueueCnt : public std::priority_queue<T, C>
|
||||
{
|
||||
static const C& getContainer(const std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
static C& getContainer(std::priority_queue<T, C>& s )
|
||||
{
|
||||
//get address of underlying container
|
||||
return s.*(&PriorityQueueCnt::c);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void test(SerializationContext& ctx, const T& v, T& r) {
|
||||
ctx.createSerializer().ext4b(v, StdQueue{10});
|
||||
ctx.createDeserializer().ext4b(r, StdQueue{10});
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdQueue, QueueDefaultContainer) {
|
||||
std::queue<int32_t> t1{};
|
||||
t1.push(3);
|
||||
t1.push(-4854);
|
||||
std::queue<int32_t> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdQueue, QueueVectorContainer) {
|
||||
std::queue<int32_t, std::vector<int32_t>> t1{};
|
||||
t1.push(3);
|
||||
t1.push(-4854);
|
||||
std::queue<int32_t, std::vector<int32_t>> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdQueue, PriorityQueueDefaultContainer) {
|
||||
std::priority_queue<int32_t> t1{};
|
||||
t1.push(3);
|
||||
t1.push(-4854);
|
||||
std::priority_queue<int32_t> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
auto & ct1 = PriorityQueueCnt<int32_t, std::vector<int32_t>>::getContainer(t1);
|
||||
auto & cr1 = PriorityQueueCnt<int32_t, std::vector<int32_t>>::getContainer(r1);
|
||||
EXPECT_THAT(ct1, Eq(cr1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdQueue, PriorityQueueDequeContainer) {
|
||||
std::priority_queue<int32_t, std::deque<int32_t>> t1{};
|
||||
t1.push(678);
|
||||
t1.push(-44);
|
||||
std::priority_queue<int32_t, std::deque<int32_t>> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
auto & ct1 = PriorityQueueCnt<int32_t, std::deque<int32_t>>::getContainer(t1);
|
||||
auto & cr1 = PriorityQueueCnt<int32_t, std::deque<int32_t>>::getContainer(r1);
|
||||
EXPECT_THAT(ct1, Eq(cr1));
|
||||
}
|
||||
78
tests/serialization_ext_std_set.cpp
Normal file
78
tests/serialization_ext_std_set.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
//MIT License
|
||||
//
|
||||
//Copyright (c) 2017 Mindaugas Vinkelis
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
|
||||
#include <bitsery/ext/std_set.h>
|
||||
#include <set>
|
||||
|
||||
using StdSet = bitsery::ext::StdSet;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
template<typename T>
|
||||
class SerializeExtensionStdSet : public testing::Test {
|
||||
public:
|
||||
using TContainer = T;
|
||||
const TContainer src = {4, 8, 48, 4, 9845, 64, 8};
|
||||
TContainer res{};
|
||||
};
|
||||
|
||||
using SerializeExtensionStdSetTypes = ::testing::Types<
|
||||
std::unordered_set<int32_t>,
|
||||
std::unordered_multiset<int32_t>,
|
||||
std::set<int32_t>,
|
||||
std::multiset<int32_t>>;
|
||||
|
||||
TYPED_TEST_CASE(SerializeExtensionStdSet, SerializeExtensionStdSetTypes);
|
||||
|
||||
TYPED_TEST(SerializeExtensionStdSet, ValuesSyntaxDifferentSetTypes) {
|
||||
SerializationContext ctx1;
|
||||
ctx1.createSerializer().ext4b(this->src, StdSet{10});
|
||||
ctx1.createDeserializer().ext4b(this->res, StdSet{10});
|
||||
EXPECT_THAT(this->res, Eq(this->src));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionStdSet, ObjectSyntax) {
|
||||
SerializationContext ctx1;
|
||||
std::set<MyStruct1> t1{MyStruct1{874 ,456}, MyStruct1{-874, -456}, MyStruct1{4894,0}};
|
||||
std::set<MyStruct1> r1{};
|
||||
ctx1.createSerializer().ext(t1, StdSet{10});
|
||||
ctx1.createDeserializer().ext(r1, StdSet{10});
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
}
|
||||
|
||||
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();
|
||||
ser.ext(t1, StdSet{10}, [&ser](int32_t& v) {
|
||||
ser.value4b(v);
|
||||
});
|
||||
auto& des = ctx1.createDeserializer();
|
||||
des.ext(r1, StdSet{10}, [&des](int32_t& v) {
|
||||
des.value4b(v);
|
||||
});
|
||||
EXPECT_THAT(r1, Eq(t1));
|
||||
}
|
||||
@@ -22,61 +22,37 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/ext/std_stack.h>
|
||||
|
||||
#if __cplusplus > 201402L
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include<optional>
|
||||
|
||||
#include <bitsery/ext/optional.h>
|
||||
|
||||
|
||||
using Optional = bitsery::ext::Optional;
|
||||
using StdStack = bitsery::ext::StdStack;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
|
||||
template <typename T>
|
||||
void test(SerializationContext& ctx, const T& v, T& r) {
|
||||
ctx.createSerializer().ext4b(v, Optional{});
|
||||
ctx.createDeserializer().ext4b(r, Optional{});
|
||||
ctx.createSerializer().ext4b(v, StdStack{10});
|
||||
ctx.createDeserializer().ext4b(r, StdStack{10});
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionOptional, EmptyOptional) {
|
||||
std::optional<int32_t> t1{};
|
||||
std::optional<int32_t> r1{};
|
||||
TEST(SerializeExtensionStdStack, DefaultContainer) {
|
||||
std::stack<int32_t> t1{};
|
||||
t1.push(3);
|
||||
t1.push(-4854);
|
||||
std::stack<int32_t> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
|
||||
|
||||
r1 = 3;
|
||||
SerializationContext ctx2;
|
||||
test(ctx2,t1, r1);
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(1));
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionOptional, OptionalHasValue) {
|
||||
std::optional<int32_t> t1{43};
|
||||
std::optional<int32_t> r1{52};
|
||||
TEST(SerializeExtensionStdStack, VectorContainer) {
|
||||
std::stack<int32_t, std::vector<int32_t>> t1{};
|
||||
t1.push(3);
|
||||
t1.push(-4854);
|
||||
std::stack<int32_t, std::vector<int32_t>> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
EXPECT_THAT(ctx1.getBufferSize(), Eq(1 + sizeof(int)));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
r1 = std::optional<int>{};
|
||||
SerializationContext ctx2;
|
||||
test(ctx2,t1, r1);
|
||||
EXPECT_THAT(ctx2.getBufferSize(), Eq(1 + sizeof(int)));
|
||||
EXPECT_THAT(t1.value(), Eq(r1.value()));
|
||||
|
||||
EXPECT_THAT(t1, Eq(r1));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().ext(t1, r1);
|
||||
ctx.createDeserializer().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.createSerializer().value1b(tmp);
|
||||
ctx.createDeserializer().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));
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
|
||||
using testing::Eq;
|
||||
using testing::StrEq;
|
||||
@@ -96,7 +98,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
z.x = X{ 234 };
|
||||
|
||||
|
||||
auto ser = ctx.createSerializer();
|
||||
auto& ser = ctx.createSerializer();
|
||||
ser.object(y);
|
||||
ser.object(z);
|
||||
|
||||
@@ -104,7 +106,7 @@ TEST(SerializeObject, GeneralConceptTest) {
|
||||
Y yres{};
|
||||
Z zres{};
|
||||
|
||||
auto des = ctx.createDeserializer();
|
||||
auto& des = ctx.createDeserializer();
|
||||
des.object(yres);
|
||||
des.object(zres);
|
||||
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
#ifndef BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
#define BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <memory>
|
||||
|
||||
#include <bitsery/bitsery.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
|
||||
/*
|
||||
* define some types for testing
|
||||
@@ -43,6 +44,9 @@ struct MyStruct1 {
|
||||
bool operator==(const MyStruct1 &rhs) const {
|
||||
return i1 == rhs.i1 && i2 == rhs.i2;
|
||||
}
|
||||
friend bool operator < (const MyStruct1 &lhs, const MyStruct1 &rhs) {
|
||||
return lhs.i1 < rhs.i1 || (lhs.i1 == rhs.i1 && lhs.i2 < rhs.i2);
|
||||
}
|
||||
|
||||
static constexpr size_t SIZE = sizeof(MyStruct1::i1) + sizeof(MyStruct1::i2);
|
||||
};
|
||||
@@ -82,22 +86,48 @@ void serialize(S&s, MyStruct2& o) {
|
||||
s.object(o.s1);
|
||||
}
|
||||
|
||||
struct SessionsEnabledConfig: public bitsery::DefaultConfig {
|
||||
static constexpr bool BufferSessionsEnabled = true;
|
||||
};
|
||||
|
||||
class SerializationContext {
|
||||
using Buffer = std::vector<char>;
|
||||
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
|
||||
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
|
||||
using Writer = bitsery::AdapterWriter<OutputAdapter, bitsery::DefaultConfig>;
|
||||
using Reader = bitsery::AdapterReader<InputAdapter, bitsery::DefaultConfig>;
|
||||
|
||||
|
||||
template <typename Config = bitsery::DefaultConfig>
|
||||
class BasicSerializationContext {
|
||||
public:
|
||||
bitsery::DefaultConfig::BufferType buf{};
|
||||
std::unique_ptr<bitsery::BufferWriter> bw;
|
||||
std::unique_ptr<bitsery::BufferReader> br;
|
||||
Buffer buf;
|
||||
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::Serializer createSerializer() {
|
||||
//make_unique is not in c++11
|
||||
bw = std::unique_ptr<bitsery::BufferWriter>(new bitsery::BufferWriter(buf));
|
||||
return bitsery::Serializer{*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::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 {
|
||||
auto range = bw->getWrittenRange();
|
||||
return std::distance(range.begin(), range.end());
|
||||
return bw->writtenBytesCount();
|
||||
}
|
||||
|
||||
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
|
||||
@@ -110,12 +140,9 @@ public:
|
||||
return 4;
|
||||
}
|
||||
|
||||
bitsery::Deserializer createDeserializer() {
|
||||
bw->flush();
|
||||
//make_unique is not in c++11
|
||||
br = std::unique_ptr<bitsery::BufferReader>(new bitsery::BufferReader(bw->getWrittenRange()));
|
||||
return bitsery::Deserializer{*br};
|
||||
};
|
||||
};
|
||||
|
||||
//helper type
|
||||
using SerializationContext = BasicSerializationContext<bitsery::DefaultConfig>;
|
||||
|
||||
#endif //BITSERY_SERIALIZER_TEST_UTILS_H
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "serialization_test_utils.h"
|
||||
#include <bitsery/traits/string.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
TEST(SerializeText, BasicString) {
|
||||
@@ -113,6 +115,5 @@ TEST(SerializeText, WhenCArrayNotNullterminatedThenAssert) {
|
||||
char16_t t1[CARR_LENGTH]{u"some text"};
|
||||
//make last character not nullterminated
|
||||
t1[CARR_LENGTH-1] = 'x';
|
||||
|
||||
EXPECT_DEATH(ctx.createSerializer().text<2>(t1), "");
|
||||
}
|
||||
Reference in New Issue
Block a user