23 Commits

Author SHA1 Message Date
Mindaugas Vinkelis
d787680819 release v5.1.0 2020-06-08 14:32:18 +03:00
domgho
a519d333e2 Fix typo (#57) 2020-05-16 20:32:33 +03:00
Nick Renieris
826b8d4269 std::atomic support (#54) 2020-05-02 22:05:04 +03:00
Mindaugas Vinkelis
16f637da0d integer casts part3 2020-04-21 10:04:09 +03:00
tower120
9cade41dbb msvc warnings suppression (#47) 2020-04-21 08:38:16 +03:00
tower120
3dc5940c16 Integer casts. Part 2. (#41) 2020-04-21 08:35:32 +03:00
tower120
a544879b22 basic_ios <=> basic_ostream (#45)
basic_ios changed to basic_ostream and basic_istream
2020-04-16 21:48:49 +03:00
tower120
d47ee834e4 fix integer cast warnings. (#38) 2020-04-15 13:17:44 +03:00
Mindaugas Vinkelis
c556c75100 fixed some include paths 2020-04-15 12:46:42 +03:00
Mindaugas Vinkelis
541632fa9e VIP tutorial on Version extension 2020-02-06 20:00:42 +02:00
Mindaugas Vinkelis
d24dfe14f5 provided read/write method for adapaters that accepts number of bytes at
compile-time
2020-01-29 15:08:25 +02:00
Mindaugas Vinkelis
ee68261124 rewriten buffer adapters, to fix UB when incrementing past the end iterator. 2020-01-27 16:21:39 +02:00
Mindaugas Vinkelis
d22b7c1527 release v5.0.2 2020-01-17 11:05:56 +02:00
Mindaugas Vinkelis
8d6ffc9873 extracted Centos7 gcc4.8.2 specific code into separate patch file 2020-01-17 09:28:26 +02:00
Arnaud Botella
0e76e0608c Fix compilation with gcc 4.8.2 (#33)
Update for gcc4.8.2 for Centos7
2020-01-17 08:08:49 +02:00
Mindaugas Vinkelis
501d60f67d Merge pull request #30 from nicktrandafil/master
fix non default constructible container deserialization
2019-11-25 09:41:04 +02:00
Nicolai Trandafil
04afd16fbd fix non default constructible container deser 2019-11-24 14:30:43 +02:00
Nicolai Trandafil
9621db1cd7 add a failing case 2019-11-24 14:24:15 +02:00
Mindaugas Vinkelis
c555088aa3 version 5.0.1 release 2019-08-21 14:03:20 +03:00
Mindaugas Vinkelis
c9619e3e3d macOS compilation fix and polymorphic handler deleter fix 2019-08-21 13:58:43 +03:00
Mindaugas Vinkelis
01d56e00b8 Merge pull request #25 from BotellaA/patch-1
Update memory_resource.h to remove compile warnings
2019-08-21 13:55:33 +03:00
Arnaud Botella
04526ff0f4 Update memory_resource.h 2019-08-01 14:59:15 +02:00
Arnaud Botella
b7d159bbfc Update memory_resource.h
To be consistent with line 135.
2019-08-01 14:43:52 +02:00
66 changed files with 941 additions and 442 deletions

View File

@@ -50,9 +50,9 @@ before_install:
- export CC=$CC_COMPILER
install:
- wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz
- tar xf release-1.8.0.tar.gz
- cd googletest-release-1.8.0
- wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz
- tar xf release-1.10.0.tar.gz
- cd googletest-release-1.10.0
- cmake -DBUILD_SHARED_LIBS=ON .
- make
- sudo make install

View File

@@ -1,3 +1,44 @@
# [5.1.0](https://github.com/fraillt/bitsery/compare/v5.0.3...v5.1.0) (2020-06-08)
### Features
* new extension **StdAtomic** (thanks for [VelocityRa](https://github.com/VelocityRa)).
### Improvements
* [examples](examples) no longer include bitsery in global namespace (removed `using namespace bitsery`).
* removed multiple warning regarding integer conversions (thanks to [tower120](https://github.com/tower120)).
* removed unnecessary `dynamic_cast` from BasicInput/OutputStreamAdapter (thanks to [tower120](https://github.com/tower120)).
* fixed some include paths, now you can basically to copy/paste bitsery include directory to your project without cmake support.
### Other notes
* added tutorial of how to write your own extension ([here])(doc/tutorial/first_extension.md).
* now gtest 1.10 is required if you want to build tests.
# [5.0.3](https://github.com/fraillt/bitsery/compare/v5.0.2...v5.0.3) (2020-01-29)
### Improvements
* rewritten buffer adapters (and `BasicBufferedOutputStreamAdapter`) to fix UB when incrementing past the end iterator, and added an additional read/write method that accepts a number of bytes to be read/written at compile time.
This provides additional optimization opportunities.
# [5.0.2](https://github.com/fraillt/bitsery/compare/v5.0.1...v5.0.2) (2020-01-17)
### Bug fixes
* fixed a bug when deserializing non-default constructible containers (thanks to [nicktrandafil](https://github.com/nicktrandafil)).
* fixed issue with a brace initialization in extension StdMap and StdSet. It was working on major compilers, but it wasn't C++11 compatible.
More info about it in [stackoverflow](https://stackoverflow.com/questions/25612262/why-does-auto-x3-deduce-an-initializer-list) (thanks to [BotellaA](https://github.com/BotellaA)).
### Other notes
* added [patches/centos7_gcc4.8.2.diff](patches/centos7_gcc4.8.2.diff) that allows to use bitsery with gcc4.8.2 on Centos7 (thanks to [BotellaA](https://github.com/BotellaA)).
More information on patches is [here](patches/README.md).
* added documentation on how [extensions](doc/design/extensions.md) work.
# [5.0.1](https://github.com/fraillt/bitsery/compare/v5.0.0...v5.0.1) (2019-08-21)
### Bug fixes
* fixed polymorphic handler deleter in `PolymorphicContext`, now it is captured by value and doesn't depend on lifetime of `PolymorphicContext` instance.
* fixed compilation errors in `ext/utils/pointer_utils.h` on macOS.
* reduced warnings on Visual Studio (thanks to [BotellaA](https://github.com/BotellaA))
# [5.0.0](https://github.com/fraillt/bitsery/compare/v4.6.1...v5.0.0) (2019-07-09)
This version reduces library complexity by removing redundant features and changing existing ones.

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.1)
project(bitsery
LANGUAGES CXX
VERSION 5.0.0)
VERSION 5.1.0)
#======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)

View File

@@ -18,28 +18,27 @@ All cross-platform requirements are enforced at compile time, so serialized data
* Configurable runtime error checking on deserialization.
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
* forward/backward compatibility for your types.
* smart and raw pointers with customizable runtime polymorphism support.
* fine-grained bit-level serialization control.
* forward/backward compatibility for your types.
* smart and raw pointers with allocators support and customizable runtime polymorphism.
* Easily extendable for any type.
* Allows brief or/and verbose syntax for better serialization control.
* Allows brief (similar to [cereal](https://uscilab.github.io/cereal/)) or/and verbose syntax for better serialization control.
* Configurable endianness support.
* No macros.
## Why to use bitsery
## Why use bitsery
Look at the numbers and features list, and decide yourself.
| | data size | serialize | deserialize |
|------------------|-----------|-----------|-------------|
| bitsery | 6913B | 1252ms | 1170ms |
| bitsery_compress | 4213B | 1445ms | 1325ms |
| boost | 11037B | 9952ms | 8767ms |
| cereal | 10413B | 6497ms | 5470ms |
| flatbuffers | 14924B | 6762ms | 2173ms |
| yas | 10463B | 1352ms | 1109ms |
| yas_compress | 7315B | 1673ms | 1598ms |
| library | data size | serialize | deserialize |
| ---------------- | --------- | --------- | ----------- |
| bitsery | 6913B | 959ms | 927ms |
| bitsery_compress | 4213B | 1282ms | 1115ms |
| boost | 11037B | 9826ms | 8313ms |
| cereal | 10413B | 6324ms | 5698ms |
| flatbuffers | 14924B | 5129ms | 2142ms |
| protobuf | 10018B | 11966ms | 13919ms |
| yas | 10463B | 1908ms | 1217ms |
*benchmarked on Ubuntu with GCC 8.3.0, more details can be found [here](https://github.com/fraillt/cpp_serializers_benchmark.git)*
@@ -65,11 +64,9 @@ void serialize(S& s, MyStruct& o) {
s.container4b(o.fs, 10);
}
using namespace bitsery;
using Buffer = std::vector<uint8_t>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
@@ -77,10 +74,10 @@ int main() {
Buffer buffer;
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}
```
@@ -103,9 +100,12 @@ Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery
This library was tested on
* Windows: Visual Studio 2015, MinGW (GCC 5.2)
* Linux: GCC 5.4, GCC 6.2, Clang 3.9
* Linux: GCC 5.4, Clang 3.9
* OS X Mavericks: AppleClang 8
There is a patch that allows using bitsery with non-fully compatible C++11 compilers.
* CentOS 7 with gcc 4.8.2.
## License
**bitsery** is licensed under the [MIT license](LICENSE).

View File

@@ -6,7 +6,7 @@ Library design:
* `valueNb instead of value`
* `brief syntax`
* `serializer/deserializer functions overloads`
* `extending library functionality`
* [extending library functionality](design/extensions.md)
* `errors handling`
* `forward/backward compatibility via Growable extension`
* [pointers](design/pointers.md)
@@ -64,8 +64,10 @@ Output adapters (buffer and stream) functions:
* `writeBytes`
* `writeBuffer`
* `flush`
* `currentyWritePos (get/set)` (buffer adapter only)
* `writtenBytesCount` (buffer adapter only)
* `currentyWritePos (get/set)` (buffer adapter only) gets/sets write position in buffer, it can jump past the buffer end, in this case buffer will be resized.
This function doesn't write any bytes.
* `writtenBytesCount` (buffer adapter only) this doesn't necessary mean how many bytes are written, but rather how many bytes in the buffer was "affected" during serialization.
E.g. if `currentyWritePos` (set) jumps from 0 to 100, and then 4 bytes are written, `writtenBytesCount` return 104, it also returns 104 if you jump in somewhere in the middle.
Tips and tricks:

View File

@@ -1,16 +1,16 @@
## Motivation
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++.
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++ that meets my requirements.
I wanted serializer that is easy to use like [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is save to use with untrusted (malicious) data, and most importantly is fast and has small binary footprint.
I wanted serializer that is easy to use as [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is safe to use with untrusted (malicious) data, and most importantly is fast and has a small binary footprint.
Furthermore I wanted full serialization control and ability to work on bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce size by large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
Furthermore, I wanted full serialization control and the ability to work on a bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce the size by a large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also adds additional library complexity.
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also add additional library complexity.
## 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.
Often people use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format.
Although JSON is very readable and very convenient when used together with dynamically typed languages such as JavaScript.
When serializing data from statically typed languages, however, JSON not only has the obvious drawback of runtime inefficiency, but also forces you to write more code to access data (counterintuitively) due to its dynamic-typing serialization system.

View File

@@ -0,0 +1,51 @@
Extensions are at the heart of bitsery. They allow implementing all sorts of things, that requires customizing serialization and deserialization flows separately.
Bitsery already provides a lot of useful extensions, which can be found [here](../../include/bitsery/ext).
Let's see what are the core components of an extension:
1. Extension class itself, which implements templated `serialize` and `deserialize` methods. These functions provide similar capabilities to `save` and `load` functions in other frameworks e.g. [cereal](https://uscilab.github.io/cereal/) or [boost](https://www.boost.org/doc/libs/1_71_0/libs/serialization/doc/index.html), but are more powerful because extension itself can store extension related data as well, that can be used for additional functionality.
```cpp
class MyExtension {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const {
...
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des& des, T& obj, Fnc&& fnc) const {
...
}
};
```
2. `ExtensionTraits` specialization for an extension, which specifies how it should be used:
```cpp
namespace bitsery {
namespace traits {
template<typename T>
struct ExtensionTraits<ext::MyExtension, T> {
using TValue = ...;
static constexpr bool SupportValueOverload = ...;
static constexpr bool SupportObjectOverload = ...;
static constexpr bool SupportLambdaOverload = ...;
};
}
}
```
Now, that we know the core components of an extension, let's see how everything fits together.
An Extension can be called in 3 different ways, and `Support...Overload` methods basically define, what call syntax can be used with a particular extension.
* `SupportValueOverload` - allows to call extension by providing the size of the value type, the same as `valueNb` function. e.g. `s.ext4b(value, MyExtension{})`.
* `SupportObjectOverload` - allows to call extension the same as simple `object` function. e.g. `s.ext(value, MyExtension{})`.
* `SupportLambdaOverload` - allows to call extension by providing a custom lambda. e.g. `s.ext(value, MyExtension{}, [](...) { ... })`.
You might wonder, how there are 3 ways to call an extension, but only one signature for `serialize` and `deserialize` functions?
This is where a `TValue` from `ExtensionTraits` and the third parameter `Fnc` in `serialize` and `deserialize` comes in.
In case of lambda overload is called, the lambda is passed straight to the serialize/deserialize function as the third parameter. In theory `SupportLambdaOverload` can be any object, not necessary a callable object.
When value overload is used, then lambda is constructed by bitsery like this `[](Serializer& s, VType &v) { s.value<VSIZE>(v); }`, where `VType` is equal to `TValue` from `ExtensionTraits`.
Similarly, when object overload is used `[](Serializer& s, VType &v) { s.object(v); }` lambda is constructed.
When there is no direct mapping from object type to its underlying value type, you can disable lambda generation for value and object overload, by setting `TValue=void`, in this case, a "dummy" lambda will be provided.

View File

@@ -1,8 +1,8 @@
The grand plan for this tutorial is to learn how to serialize/deserialize any object efficiently in time and space, so you could focus on other, more interesting things.
This tutorial will cover these main topics:
* `Hello World` write one control flow for both: serialization and deserialization.
* `Composer` efficiently compose complex serialization flows.
* [Getting started](hello_world.md) with bitsery, and serialize/deserialize your first object.
* [Extend to your needs](first_extension.md) by enabling serialization/deserialization depending on version number.
* `Squeeze Me!` compress your data when you know what it stores.
* `Anything is Possible` extend library for custom container, compress geometry and more.
* `Little or Big` change endianness if you want best performance on PowerPC.

View File

@@ -1,3 +0,0 @@
*document in progress*
* explain why *value* and *object* is fundamental functions.
* write about *Growable* extension

View File

@@ -0,0 +1,67 @@
... TODO explain step-by-step what we need and how to get there
Instead I immediately provide implementation for an extension.
```cpp
#include "../details/adapter_common.h"
#include "../traits/core/traits.h"
namespace bitsery {
namespace ext {
template<size_t VERSION>
class Version {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &v, Fnc &&fnc) const {
details::writeSize(ser.adapter(), VERSION);
fnc(ser, const_cast<T&>(v), VERSION);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &v, Fnc &&fnc) const {
size_t version{};
details::readSize(des.adapter(), version, 0u, std::false_type{});
fnc(des, v, version);
}
};
}
namespace traits {
template<typename T, size_t V>
struct ExtensionTraits<ext::Version<V>, T> {
using TValue = T;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = true;
};
}
}
```
Adding such extension to the bitsery itself is impractical because it is very easy to implement, but at the same time it has a lot of customization options that actual user might require e.g.:
* how do you want to handle reading/writing version number? (in this case use compact representation as size, but do not check for errors if version number is too large)
* maybe you want to be able to set ReaderError, when version is larger than deserialization implementation handles. (we simply ignore this case and later we'll probably get reading error later anyway)
* or maybe you want to wrap object in `Growable` extension? so that you could ignore unknown fields in newer version of object.
Example of how to use this provided implementation:
```cpp
struct TypeV2 {
uint16_t x{};
uint16_t y{};
};
template <typename S>
void serialize(S& ser, TypeV2& obj) {
ser.ext(obj, bitsery::ext::Version<2u>{}, [](S& s, TypeV2&o, size_t version) {
s.value2b(o.x);
if (version == 2u) {
s.value2b(o.y);
}
});
}
```

View File

@@ -18,11 +18,9 @@ There's nothing to build or make - **bitsery** is header only.
#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>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
```
@@ -62,7 +60,7 @@ void serialize(S& s, MyStruct& o) {
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.text1b(o.str);** serialize text (null-terminated) of char type, if you use *wchar* then you would write *text2b* or *text4b* depending on the OS platform.
* **s.container4b(o.fs, 100);** serializes any container of fundamental types of size 4bytes, **100** is max size of container.
**Bitsery** is designed to be save with untrusted (malicious) data from network, so for dynamic containers you always need to provide max possible size available, to avoid buffer-overflow attacks.
**text** didn't had this max size specified, because it was serializing fixed size container.
@@ -76,8 +74,8 @@ Create buffer and use helper functions for serialization and deserialization.
```cpp
Buffer buffer;
auto writtenSize = quickSerialization(OutputAdapter{buffer}, data);
auto state = quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
```
These helper functions use default configuration *bitsery::DefaultConfig*
@@ -93,11 +91,9 @@ deserialization state has two properties, error code and bool that indicates if
#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>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
struct MyStruct {
uint32_t i;
@@ -110,17 +106,17 @@ void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.text1b(o.str);
s.container4b(o.fs, 100);
};
}
int main() {
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
MyStruct res{};
Buffer buffer;
auto writtenSize = quickSerialization(OutputAdapter{buffer}, data);
auto state = quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
}
```

View File

@@ -22,12 +22,10 @@ void serialize(S& s, MyStruct& o) {
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>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
@@ -39,12 +37,12 @@ int main() {
//use quick serialization function,
//it will use default configuration to setup all the nesessary steps
//and serialize data to container
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
//same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}

View File

@@ -41,12 +41,10 @@ namespace MyTypes {
}
}
using namespace bitsery;
//use fixed-size buffer
using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
@@ -55,10 +53,10 @@ int main() {
//create buffer to store data to
Buffer buffer{};
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -23,12 +23,10 @@ struct MyStruct {
};
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
@@ -37,10 +35,10 @@ int main() {
//serialization, deserialization flow is unchanged as in basic usage
Buffer buffer;
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}

View File

@@ -29,27 +29,24 @@ void serialize(S& s, MyStruct& o) {
using MyTuple = std::tuple<float, MyStruct>;
using MyVariant = std::variant<int64_t, MyTuple, MyStruct>;
// for convenience
using namespace bitsery;
// define default serialize function for MyVariant, so that we could use quickSerialization/Deserialization functions
template<typename S>
void serialize(S& s, MyVariant& o) {
// in order to serialize a variant, it needs to know how to do it for all types
// we can do this simply by providing any callable object, that accepts serializer and type as arguments
s.ext(o, ext::StdVariant{
s.ext(o, bitsery::ext::StdVariant{
// specify how to serialize tuple by creating a lambda
[](S& s, MyTuple& o) {
// StdTuple is used exactly the same as StdVariant
s.ext(o, ext::StdTuple{
s.ext(o, bitsery::ext::StdTuple{
// this is convenient callable object to specify integral value size
// it is different equivalent to lambda [](auto& s, float&o) { s.value4b(o);}
ext::OverloadValue<float, 4>{},
bitsery::ext::OverloadValue<float, 4>{},
// it is not required to provide MyStruct overload, because it we have defined 'serialize' function for it
});
},
// this might also be useful if you want to overload using extension
ext::OverloadExtValue<int64_t, 8, ext::CompactValue>{},
bitsery::ext::OverloadExtValue<int64_t, 8, bitsery::ext::CompactValue>{},
// you can even go further and instead of writing lambda for MyTuple you can as well compose the same functionality
// with OverloadExtObject, like this:
// (comment out MyTuple lambda, and uncomment this)
@@ -59,7 +56,7 @@ void serialize(S& s, MyVariant& o) {
[](S& s, MyStruct& o) {
s.value4b(o.f);
s.container(o.v, 1000, [](S& s, int32_t& v) {
s.ext4b(v, ext::CompactValue{});
s.ext4b(v, bitsery::ext::CompactValue{});
});
},
// NOTE.
@@ -78,8 +75,8 @@ void serialize(S& s, MyVariant& o) {
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
@@ -93,13 +90,13 @@ int main() {
//use quick serialization function,
//it will use default configuration to setup all the nesessary steps
//and serialize data to container
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
//same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data == res);
}
#else

View File

@@ -67,10 +67,9 @@ using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
//use fixed-size buffer
using Buffer = std::vector<uint8_t>;
using namespace bitsery;
// define adapter types,
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
@@ -90,10 +89,10 @@ int main() {
//create buffer to store data to
Buffer buffer{};
auto writtenSize = quickSerialization(ctx, OutputAdapter{buffer}, data);
auto writtenSize = bitsery::quickSerialization(ctx, OutputAdapter{buffer}, data);
MyTypes::GameState res{};
auto state = quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -19,8 +19,6 @@ void serialize(S& s, MyStruct& o) {
s.value8b(o.f);
}
using namespace bitsery;
int main() {
//set some random data
MyStruct data{8941, MyEnum::V2, 0.045};
@@ -35,7 +33,7 @@ int main() {
}
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
Serializer<OutputBufferedStreamAdapter> ser{s};
bitsery::Serializer<bitsery::OutputBufferedStreamAdapter> ser{s};
ser.object(data);
//flush to writer
ser.adapter().flush();
@@ -50,8 +48,8 @@ int main() {
//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<InputStreamAdapter>(s, res);
auto state = bitsery::quickDeserialization<bitsery::InputStreamAdapter>(s, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.f == res.f && data.i == res.i && data.e == res.e);
}

View File

@@ -68,12 +68,10 @@ namespace MyTypes {
}
}
using namespace bitsery;
//use fixed-size buffer
using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = OutputBufferAdapter<Buffer>;
using InputAdapter = InputBufferAdapter<Buffer>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
@@ -84,11 +82,11 @@ int main() {
//create buffer to store data to
Buffer buffer{};
//since we're using different configuration, we cannot use quickSerialization function.
auto writtenSize = quickSerialization<OutputAdapter>(buffer, data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
//deserialize
auto state = quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

View File

@@ -78,13 +78,10 @@ namespace bitsery {
struct SelectSerializeFnc<MultipleInheritance>:UseNonMemberFnc {};
}
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = OutputBufferAdapter<Buffer>;
using Reader = InputBufferAdapter<Buffer>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
@@ -95,13 +92,13 @@ int main() {
Buffer buf{};
ext::InheritanceContext ctx1;
auto writtenSize = quickSerialization(ctx1, Writer{buf}, data);
bitsery::ext::InheritanceContext ctx1;
auto writtenSize = bitsery::quickSerialization(ctx1, Writer{buf}, data);
assert(writtenSize == 4);//base is serialized once, because it is inherited virtually
MultipleInheritance res{0};
ext::InheritanceContext ctx2;
auto state = quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
assert(state.first == ReaderError::NoError && state.second);
bitsery::ext::InheritanceContext ctx2;
auto state = bitsery::quickDeserialization(ctx2, Reader{buf.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.x == res.x && data.y1 == res.y1 && data.getY2() == res.getY2() && data.z == res.z);
}

View File

@@ -30,12 +30,10 @@ public:
}
};
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = OutputBufferAdapter<Buffer>;
using Reader = InputBufferAdapter<Buffer>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
@@ -49,16 +47,16 @@ int main() {
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
//create writer and serialize container
Serializer<Writer> ser{buffer};
bitsery::Serializer<Writer> ser{buffer};
ser.container(data, 10);
ser.adapter().flush();
//create reader and deserialize container
Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
bitsery::Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
des.container(res, 10);
//check if everything went ok
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(res == data);
}

View File

@@ -78,12 +78,10 @@ private:
}
};
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = OutputBufferAdapter<Buffer>;
using Reader = InputBufferAdapter<Buffer>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
//we will need PointerLinkingContext to work with pointers
//if we would require additional context for our own custom flow, we can define it as tuple like this:
@@ -114,7 +112,7 @@ int main() {
size_t writtenSize{};
//in order to use pointers, we need to pass pointer linking context serializer/deserializer
{
ext::PointerLinkingContext ctx{};
bitsery::ext::PointerLinkingContext ctx{};
writtenSize = quickSerialization(ctx, Writer{buffer}, data);
//make sure that pointer linking context is valid
@@ -125,10 +123,10 @@ int main() {
Test1Data res{};
{
ext::PointerLinkingContext ctx{};
bitsery::ext::PointerLinkingContext ctx{};
auto state = quickDeserialization(ctx, Reader{buffer.begin(), writtenSize}, res);
//check if everything went find
assert(state.first == ReaderError::NoError && state.second);
assert(state.first == bitsery::ReaderError::NoError && state.second);
//also check for dangling pointers, after deserialization
assert(ctx.isValid());
}

View File

@@ -181,23 +181,21 @@ namespace bitsery {
// also it automatically ensures, that classes is registered in the same order for serialization and deserialization
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
//use bitsery namespace for convenience
using namespace bitsery;
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = OutputBufferAdapter<Buffer>;
using Reader = InputBufferAdapter<Buffer>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
//we need to define few things in order to work with polymorphism
//1) we need pointer linking context to work with pointers
//2) we need polymorphic context to be able to work with polymorphic types
using TContext = std::tuple<ext::PointerLinkingContext, ext::PolymorphicContext<ext::StandardRTTI>>;
using TContext = std::tuple<
bitsery::ext::PointerLinkingContext,
bitsery::ext::PolymorphicContext<bitsery::ext::StandardRTTI>>;
//NOTE:
// RTTI can be customizable, if you can't use dynamic_cast and typeid, and have 'custom' solution
using MySerializer = Serializer<Writer, TContext>;
using MyDeserializer = Deserializer<Reader, TContext>;
using MySerializer = bitsery::Serializer<Writer, TContext>;
using MyDeserializer = bitsery::Deserializer<Reader, TContext>;
//checks if deserialized data is equal
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
@@ -257,7 +255,7 @@ int main() {
//deserialize our data
MyDeserializer des{ctx, buffer.begin(), writtenSize};
des.object(res);
assert(des.adapter().error() == ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
//also check for dangling pointers, after deserialization
assert(std::get<0>(ctx).isValid());
// clear shared state from pointer linking context,

View File

@@ -28,57 +28,27 @@
namespace bitsery {
template<typename Buffer>
class BufferIterators {
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
using BuffNonConst = typename std::remove_const<Buffer>::type;
public:
BufferIterators(const BufferIterators&) = delete;
BufferIterators& operator=(const BufferIterators&) = delete;
BufferIterators(BufferIterators&&) = default;
BufferIterators& operator=(BufferIterators&&) = default;
virtual ~BufferIterators() = default;
protected:
using TIterator = typename std::conditional<isConstBuffer,
typename traits::BufferAdapterTraits<BuffNonConst>::TConstIterator,
typename traits::BufferAdapterTraits<BuffNonConst>::TIterator>::type;
static_assert(details::IsDefined<TIterator>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
BufferIterators(TIterator begin, TIterator end)
: beginIt{begin},
posIt{begin},
endIt{end} {
}
TIterator beginIt;
TIterator posIt;
TIterator endIt;
};
template<typename Buffer, typename Config = DefaultConfig>
class InputBufferAdapter: public BufferIterators<Buffer>,
public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
class InputBufferAdapter: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
public:
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>>;
using TConfig = Config;
using TIterator = typename BufferIterators<Buffer>::TIterator;
using TIterator = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TConstIterator;
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
static_assert(details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
"BufferAdapter only works with contiguous containers");
InputBufferAdapter(TIterator begin, TIterator endIt)
: BufferIterators<Buffer>(begin, endIt),
_endReadPos{endIt} {
InputBufferAdapter(TIterator beginIt, size_t size)
: _beginIt{beginIt},
_currOffset{0},
_endReadOffset{size},
_bufferSize{size} {
};
}
InputBufferAdapter(TIterator begin, size_t size)
: BufferIterators<Buffer>(begin, std::next(begin, size)),
_endReadPos{std::next(begin, size)} {
InputBufferAdapter(TIterator beginIt, TIterator endIt)
:InputBufferAdapter(beginIt, static_cast<size_t>(std::distance(beginIt, endIt))) {
}
InputBufferAdapter(const InputBufferAdapter&) = delete;
@@ -92,18 +62,17 @@ namespace bitsery {
}
size_t currentReadPos() const {
return static_cast<size_t>(std::distance(this->beginIt, this->posIt));
return currentReadPosChecked(std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void currentReadEndPos(size_t pos) {
// assert that CheckAdapterErrors is enabled, otherwise it will simply will not work even if data and buffer is not corrupted
static_assert(Config::CheckAdapterErrors, "Please enable CheckAdapterErrors to use this functionality.");
const auto buffSize = static_cast<size_t>(std::distance(this->beginIt, this->endIt));
if (buffSize >= pos) {
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_overflowOnReadEndPos = pos == 0;
if (pos == 0)
pos = buffSize;
_endReadPos = std::next(this->beginIt, pos);
pos = _bufferSize;
_endReadOffset = pos;
} else {
error(ReaderError::DataOverflow);
}
@@ -112,44 +81,74 @@ namespace bitsery {
size_t currentReadEndPos() const {
if (_overflowOnReadEndPos)
return 0;
return static_cast<size_t>(std::distance(this->beginIt, _endReadPos));
return _endReadOffset;
}
ReaderError error() const {
return _err;
return _currOffset <= _endReadOffset
? ReaderError::NoError
: static_cast<ReaderError>(_currOffset - _endReadOffset);
}
void error(ReaderError error) {
if (_err == ReaderError::NoError) {
_err = error;
_endReadPos = this->endIt;
this->beginIt = this->endIt;
this->posIt = this->endIt;
if (_currOffset <= _endReadOffset) {
_endReadOffset = 0;
_bufferSize = 0;
_currOffset = static_cast<size_t>(error);
}
}
bool isCompletedSuccessfully() const {
return this->posIt == this->endIt && _err == ReaderError::NoError;
return _currOffset == _bufferSize;
}
private:
using diff_t = typename std::iterator_traits<TIterator>::difference_type;
void readChecked(TValue *data, size_t size, std::false_type) {
//for optimization
auto tmp = this->posIt;
this->posIt += size;
assert(std::distance(this->posIt, this->endIt) >= 0);
std::memcpy(data, std::addressof(*tmp), size);
template <size_t SIZE>
void readInternalValue(TValue *data) {
readInternalValueChecked<SIZE>(data, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readChecked(TValue *data, size_t size, std::true_type) {
//for optimization
auto tmp = this->posIt;
this->posIt += size;
if (std::distance(this->posIt, _endReadPos) >= 0) {
std::memcpy(data, std::addressof(*tmp), size);
void readInternalBuffer(TValue *data, size_t size) {
readInternalBufferChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
template <size_t SIZE>
void readInternalValueChecked(TValue *data, std::false_type) {
const size_t newOffset = _currOffset + SIZE;
assert(newOffset <= _endReadOffset);
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), SIZE, data);
_currOffset = newOffset;
}
template <size_t SIZE>
void readInternalValueChecked(TValue *data, std::true_type) {
const size_t newOffset = _currOffset + SIZE;
if (newOffset <= _endReadOffset) {
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), SIZE, data);
_currOffset = newOffset;
} else {
//set everything to zeros
std::memset(data, 0, SIZE);
if (_overflowOnReadEndPos)
error(ReaderError::DataOverflow);
}
}
void readInternalBufferChecked(TValue *data, size_t size, std::false_type) {
const size_t newOffset = _currOffset + size;
assert(newOffset <= _endReadOffset);
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), size, data);
_currOffset = newOffset;
}
void readInternalBufferChecked(TValue *data, size_t size, std::true_type) {
const size_t newOffset = _currOffset + size;
if (newOffset <= _endReadOffset) {
std::copy_n(_beginIt + static_cast<diff_t>(_currOffset), size, data);
_currOffset = newOffset;
} else {
this->posIt -= size;
//set everything to zeros
std::memset(data, 0, size);
if (_overflowOnReadEndPos)
@@ -157,24 +156,31 @@ namespace bitsery {
}
}
void readInternal(TValue *data, size_t size) {
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void currentReadPosChecked(size_t pos, std::true_type) {
if (static_cast<size_t>(std::distance(this->beginIt, this->endIt)) >= pos) {
this->posIt = std::next(this->beginIt, pos);
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_currOffset = pos;
} else {
error(ReaderError::DataOverflow);
}
}
void currentReadPosChecked(size_t pos, std::false_type) {
this->posIt = std::next(this->beginIt, pos);
_currOffset = pos;
}
TIterator _endReadPos;
ReaderError _err = ReaderError::NoError;
size_t currentReadPosChecked(std::true_type) const {
return error() == ReaderError::NoError ? _currOffset : 0;
}
size_t currentReadPosChecked(std::false_type) const {
return _currOffset;
}
TIterator _beginIt;
size_t _currOffset;
size_t _endReadOffset;
size_t _bufferSize;
bool _overflowOnReadEndPos = true;
};
@@ -192,8 +198,8 @@ namespace bitsery {
"BufferAdapter only works with contiguous containers");
OutputBufferAdapter(Buffer &buffer)
: _buffer{std::addressof(buffer)} {
: _buffer{std::addressof(buffer)},
_beginIt{std::begin(buffer)} {
init(TResizable{});
}
@@ -203,8 +209,7 @@ namespace bitsery {
OutputBufferAdapter& operator = (OutputBufferAdapter&&) = default;
void currentWritePos(size_t pos) {
const auto currPos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
const auto maxPos = currPos > pos ? currPos : pos;
const auto maxPos = _currOffset > pos ? _currOffset : pos;
if (maxPos > _biggestCurrentPos) {
_biggestCurrentPos = maxPos;
}
@@ -212,7 +217,7 @@ namespace bitsery {
}
size_t currentWritePos() const {
return static_cast<size_t> (std::distance(std::begin(*_buffer), _outIt));
return _currOffset;
}
void flush() {
@@ -220,21 +225,27 @@ namespace bitsery {
}
size_t writtenBytesCount() const {
const auto pos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
return pos > _biggestCurrentPos ? pos : _biggestCurrentPos;
return _currOffset > _biggestCurrentPos ? _currOffset : _biggestCurrentPos;
}
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
using diff_t = typename std::iterator_traits<TIterator>::difference_type;
void writeInternal(const TValue *data, size_t size) {
writeInternalImpl(data, size, TResizable{});
template <size_t SIZE>
void writeInternalValue(const TValue *data) {
writeInternalValueImpl<SIZE>(data, TResizable{});
}
void writeInternalBuffer(const TValue *data, size_t size) {
writeInternalBufferImpl(data, size, TResizable{});
}
Buffer* _buffer;
TIterator _outIt{};
TIterator _end{};
size_t _biggestCurrentPos{};
TIterator _beginIt;
size_t _currOffset{0};
size_t _bufferSize{0};
size_t _biggestCurrentPos{0};
/*
* resizable buffer
@@ -245,47 +256,40 @@ namespace bitsery {
if (traits::ContainerTraits<Buffer>::size(*_buffer) == 0u) {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
}
_end = std::end(*_buffer);
_outIt = std::begin(*_buffer);
updateIteratorAndSize();
}
void writeInternalImpl(const TValue *data, const size_t size, std::true_type) {
//optimization
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
if (std::distance(_outIt , _end) >= static_cast<TDistance>(size)) {
std::memcpy(std::addressof(*_outIt), data, size);
_outIt += size;
#else
auto tmp = _outIt;
_outIt += size;
if (std::distance(_outIt, _end) >= 0) {
std::memcpy(std::addressof(*tmp), data, size);
#endif
template <size_t SIZE>
void writeInternalValueImpl(const TValue *data, std::true_type) {
const size_t newOffset = _currOffset + SIZE;
if (newOffset <= _bufferSize) {
std::copy_n(data, SIZE, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
} else {
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
#else
_outIt -= size;
#endif
//get current position before invalidating iterators
const auto pos = std::distance(std::begin(*_buffer), _outIt);
//increase container size
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
//restore iterators
_end = std::end(*_buffer);
_outIt = std::next(std::begin(*_buffer), pos);
updateIteratorAndSize();
writeInternalValueImpl<SIZE>(data, std::true_type{});
}
}
writeInternalImpl(data, size, std::true_type{});
void writeInternalBufferImpl(const TValue *data, const size_t size, std::true_type) {
const size_t newOffset = _currOffset + size;
if (newOffset <= _bufferSize) {
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
} else {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
updateIteratorAndSize();
writeInternalBufferImpl(data, size, std::true_type{});
}
}
void setCurrentWritePos(size_t pos, std::true_type) {
const auto begin = std::begin(*_buffer);
if (static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos) {
_outIt = std::next(begin, pos);
if (pos <= _bufferSize) {
_currOffset = pos;
} else {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
updateIteratorAndSize();
setCurrentWritePos(pos, std::true_type{});
}
}
@@ -294,22 +298,32 @@ namespace bitsery {
* non resizable buffer
*/
void init(std::false_type) {
_outIt = std::begin(*_buffer);
_end = std::end(*_buffer);
updateIteratorAndSize();
}
void writeInternalImpl(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);
template <size_t SIZE>
void writeInternalValueImpl(const TValue *data, std::false_type) {
const size_t newOffset = _currOffset + SIZE;
assert(newOffset <= _bufferSize);
std::copy_n(data, SIZE, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
void writeInternalBufferImpl(const TValue *data, size_t size, std::false_type) {
const size_t newOffset = _currOffset + size;
assert(newOffset <= _bufferSize);
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
void setCurrentWritePos(size_t pos, std::false_type) {
const auto begin = std::begin(*_buffer);
assert(static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos);
_outIt = std::next(begin, pos);
assert(pos <= _bufferSize);
_currOffset = pos;
}
void updateIteratorAndSize() {
_beginIt = std::begin(*_buffer);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
}
};

View File

@@ -83,12 +83,17 @@ namespace bitsery {
private:
void readInternal(TValue* data, size_t size) {
template <size_t SIZE>
void readInternalValue(TValue* data) {
readChecked(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalBuffer(TValue* data, size_t size) {
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readChecked(TValue* data, size_t size, std::true_type) {
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(data, size)) != _zeroIfNoErrors) {
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(data, static_cast<std::streamsize>(size))) != _zeroIfNoErrors) {
*data = {};
if (_zeroIfNoErrors == 0) {
error(_ios->rdstate() == std::ios_base::badbit
@@ -99,7 +104,7 @@ namespace bitsery {
}
void readChecked(TValue* data, size_t size, std::false_type) {
_ios->rdbuf()->sgetn(data , size);
_ios->rdbuf()->sgetn(data , static_cast<std::streamsize>(size));
}
std::basic_ios<TChar, CharTraits>* _ios;
@@ -114,8 +119,8 @@ namespace bitsery {
using TConfig = Config;
using TValue = TChar;
BasicOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream)
:_ios{std::addressof(ostream)} {}
BasicOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream)
:_ostream{std::addressof(ostream)} {}
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
@@ -127,8 +132,7 @@ namespace bitsery {
}
void flush() {
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
ostream->flush();
_ostream->flush();
}
size_t writtenBytesCount() const {
@@ -139,12 +143,16 @@ namespace bitsery {
private:
void writeInternal(const TValue* data, size_t size) {
//for optimization
_ios->rdbuf()->sputn( data , size );
template <size_t SIZE>
void writeInternalValue(const TValue* data) {
_ostream->rdbuf()->sputn( data , SIZE );
}
std::basic_ios<TChar, CharTraits>* _ios;
void writeInternalBuffer(const TValue* data, size_t size) {
_ostream->rdbuf()->sputn( data , size );
}
std::basic_ostream<TChar, CharTraits>* _ostream;
};
template <typename TChar, typename Config, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
@@ -160,12 +168,15 @@ namespace bitsery {
using TValue = TChar;
//bufferSize is used when buffer is dynamically allocated
BasicBufferedOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream, size_t bufferSize = 256)
:_ios(std::addressof(ostream)),
BasicBufferedOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream, size_t bufferSize = 256)
:_ostream(std::addressof(ostream)),
_buf{},
_outIt{}
_beginIt{std::begin(_buf)},
_currOffset{0}
{
init(bufferSize, TResizable{});
// buffer size must be atleast 16, because writeIntervalValue expect that at least one value fits to buffer.
assert(_bufferSize >= 16);
}
//we need to explicitly declare move logic, because after move buffer might be invalidated
@@ -173,21 +184,20 @@ namespace bitsery {
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
: _ios{rhs._ios},
_buf{},
_outIt{}
: _ostream{rhs._ostream},
_buf{std::move(rhs._buf)},
_beginIt{std::begin(_buf)},
_currOffset{rhs._currOffset},
_bufferSize{rhs._bufferSize}
{
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
_buf = std::move(rhs._buf);
_outIt = std::next(std::begin(_buf), size);
};
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
_ios = rhs._ios;
//get current written size, before move
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
_ostream = rhs._ostream;
_buf = std::move(rhs._buf);
_outIt = std::next(std::begin(_buf), size);
_beginIt = std::begin(_buf);
_currOffset = rhs._currOffset;
_bufferSize = rhs._bufferSize;
return *this;
};
@@ -201,11 +211,8 @@ namespace bitsery {
}
void flush() {
auto begin = std::begin(_buf);
writeToStream(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
_outIt = begin;
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
ostream->flush();
writeBufferToStream();
_ostream->flush();
}
size_t writtenBytesCount() const {
@@ -216,64 +223,73 @@ namespace bitsery {
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
using diff_t = typename std::iterator_traits<BufferIt>::difference_type;
void writeInternal(const TValue* data, size_t size) {
auto tmp = _outIt;
template <size_t SIZE>
void writeInternalValue(const TValue* data) {
auto newOffset = _currOffset + SIZE;
if (newOffset > _bufferSize) {
writeBufferToStream();
newOffset = SIZE;
}
std::copy_n(data, SIZE, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
std::memcpy(std::addressof(*_outIt), data, size);
_outIt += size;
}
#else
_outIt += size;
if (std::distance(_outIt , std::end(_buf)) >= 0) {
std::memcpy(std::addressof(*tmp), data, size);
}
#endif
else {
//when buffer is full write out to stream
_outIt = std::begin(_buf);
writeToStream(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
writeToStream(data, size);
void writeInternalBuffer(const TValue* data, size_t size) {
const auto newOffset = _currOffset + size;
if (newOffset <= _bufferSize) {
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
} else {
writeBufferToStream();
// write buffer directly to stream
_ostream->rdbuf()->sputn(data, size);
}
}
void writeToStream(const TValue* data, size_t size) {
_ios->rdbuf()->sputn( data , size );
void writeBufferToStream() {
_ostream->rdbuf()->sputn(std::addressof(*_beginIt), static_cast<std::streamsize>(_currOffset));
_currOffset = 0;
}
void init (size_t bufferSize, std::true_type) {
_buf.resize(bufferSize);
_outIt = std::begin(_buf);
void init (size_t buffSize, std::true_type) {
// resize buffer
_bufferSize = buffSize;
_buf.resize(_bufferSize);
_beginIt = std::begin(_buf);
}
void init (size_t, std::false_type) {
_outIt = std::begin(_buf);
void init (size_t , std::false_type) {
// ignore buffer size parameter, and instead take actual buffer size
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
}
std::basic_ios<TChar, CharTraits>* _ios;
std::basic_ostream<TChar, CharTraits>* _ostream;
TBuffer _buf;
BufferIt _outIt;
BufferIt _beginIt;
size_t _currOffset;
size_t _bufferSize{0};
};
template <typename TChar, typename Config, typename CharTraits>
class BasicIOStreamAdapter:public BasicInputStreamAdapter<TChar, Config, CharTraits>, public BasicOutputStreamAdapter<TChar, Config, CharTraits> {
class BasicIOStreamAdapter
: public BasicInputStreamAdapter<TChar, Config, CharTraits>
, public BasicOutputStreamAdapter<TChar, Config, CharTraits>
{
public:
using TValue = TChar;
//both bases contain reference to same iostream, so no need to do anything
BasicIOStreamAdapter(std::basic_ios<TChar, CharTraits>& iostream)
:BasicInputStreamAdapter<TChar, Config, CharTraits>{iostream},
BasicOutputStreamAdapter<TChar, Config, CharTraits>{iostream} {
}
BasicIOStreamAdapter(std::basic_iostream<TChar, CharTraits>& iostream)
:BasicInputStreamAdapter<TChar, Config, CharTraits>{iostream}
,BasicOutputStreamAdapter<TChar, Config, CharTraits>{iostream}
{}
};
//helper types for most common implementations for std streams
using OutputStreamAdapter = BasicOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using InputStreamAdapter = BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using IOStreamAdapter = BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using InputStreamAdapter = BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using IOStreamAdapter = BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
}

View File

@@ -25,7 +25,7 @@
#define BITSERY_BITSERY_H
#define BITSERY_MAJOR_VERSION 5
#define BITSERY_MINOR_VERSION 0
#define BITSERY_MINOR_VERSION 1
#define BITSERY_PATCH_VERSION 0
#define BITSERY_QUOTE_MACRO(name) #name

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_H
#include "details/serialization_common.h"
#include "bitsery/details/brief_syntax_common.h"
#include "details/brief_syntax_common.h"
namespace bitsery {

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
#include "../traits/array.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, size_t N>

View File

@@ -0,0 +1,35 @@
//MIT License
//
//Copyright (c) 2020 Nick Renieris
//
//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_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H
#include "../ext/std_atomic.h"
namespace bitsery {
template<typename S, typename T>
void serialize(S &s, std::atomic<T> &obj) {
s.template ext<sizeof(T)>(obj, ext::StdAtomic{});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_ATOMIC_H

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
#include "../traits/deque.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
#include "../traits/forward_list.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
#include "../traits/list.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
#include "../traits/string.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename CharT, typename Traits, typename Allocator>

View File

@@ -25,7 +25,7 @@
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
#include "../traits/vector.h"
#include "bitsery/details/brief_syntax_common.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>

View File

@@ -130,7 +130,8 @@ namespace bitsery {
template<typename T>
void readBitsInternal(T &v, size_t size) {
auto bitsLeft = size;
T res{};
using TFast = typename FastType<T>::type;
TFast res{};
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
@@ -141,12 +142,12 @@ namespace bitsery {
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
res |= shiftedRes;
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
m_scratch >>= bits;
m_scratchBits -= bits;
bitsLeft -= bits;
}
v = res;
v = static_cast<T>(res);
}
void handleAlignErrors(ScratchType value, std::true_type) {
@@ -443,8 +444,10 @@ namespace bitsery {
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)
this->_adapter.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), std::distance(first, last));
if (first != last){
const auto distance = std::distance(first, last);
this->_adapter.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), static_cast<size_t>(distance));
}
}
//process by calling functions
@@ -465,7 +468,8 @@ namespace bitsery {
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);
using diff_t = typename std::iterator_traits<decltype(begin)>::difference_type;
auto end = std::next(begin, static_cast<diff_t>(length));
procContainer<VSIZE>(begin, end, std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
//null terminated character at the end
if (traits::TextTraits<T>::addNUL)

View File

@@ -81,9 +81,8 @@ namespace bitsery {
void handleReadMaxSize(Reader&, size_t&, size_t, std::false_type) {
}
template <typename Writter>
void writeSize(Writter& w, const size_t size) {
template <typename Writer>
void writeSize(Writer& w, const size_t size) {
if (size < 0x80u) {
w.template writeBytes<1>(static_cast<uint8_t>(size));
} else {
@@ -125,7 +124,7 @@ namespace bitsery {
}
static uint16_t exec(uint16_t value) {
return (value & 0x00ff) << 8 | (value & 0xff00) >> 8;
return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8);
}
static uint8_t exec(uint8_t value) {
@@ -139,17 +138,20 @@ namespace bitsery {
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));
return static_cast<TValue>(SwapImpl::exec(static_cast<UT>(value)));
}
/**
* endianness utils
*/
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
// add test data in separate struct, because some compilers only support constexpr functions with return-only body
// suppress msvc warnings.
#pragma warning( disable : 4310 )
struct EndiannessTestData {
static constexpr uint32_t _sample4Bytes = 0x01020304;
static constexpr uint8_t _sample1stByte = (const uint8_t &) _sample4Bytes;
};
#pragma warning( default : 4310 )
constexpr EndiannessType getSystemEndianness() {
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01,
@@ -176,9 +178,55 @@ namespace bitsery {
template<>
struct ScratchType<uint8_t> {
using type = uint16_t;
using type = uint_fast16_t;
};
template<typename T>
struct FastType {
using type = T;
};
template<>
struct FastType<uint8_t> {
using type = uint_fast8_t;
};
template<>
struct FastType<uint16_t> {
using type = uint_fast16_t;
};
template<>
struct FastType<uint32_t> {
using type = uint_fast32_t;
};
template<>
struct FastType<uint64_t> {
using type = uint_fast64_t;
};
template<>
struct FastType<int8_t> {
using type = int_fast8_t;
};
template<>
struct FastType<int16_t> {
using type = int_fast16_t;
};
template<>
struct FastType<int32_t> {
using type = int_fast32_t;
};
template<>
struct FastType<int64_t> {
using type = int_fast64_t;
};
/**
* output/input adapter base that handles endianness
*/
@@ -192,15 +240,14 @@ namespace bitsery {
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
writeSwapped(&v, 1, ShouldSwap<typename Adapter::TConfig>{});
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig>{});
}
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, "");
writeSwapped(buf, count, ShouldSwap<typename Adapter::TConfig>{});
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{});
}
template<typename T>
@@ -222,16 +269,27 @@ namespace bitsery {
private:
template<typename T>
void writeSwapped(const T *v, size_t count, std::true_type) {
void writeSwappedValue(const T *v, std::true_type) {
const auto res = details::swap(*v);
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
}
template<typename T>
void writeSwappedValue(const T *v, std::false_type) {
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(v));
}
template<typename T>
void writeSwappedBuffer(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);
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(&res), sizeof(T));
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
});
}
template<typename T>
void writeSwapped(const T *v, size_t count, std::false_type) {
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
void writeSwappedBuffer(const T *v, size_t count, std::false_type) {
static_cast<Adapter*>(this)->writeInternalBuffer(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
}
};
@@ -245,14 +303,16 @@ namespace bitsery {
void readBytes(T& v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
directRead(&v, 1);
static_cast<Base*>(this)->template readInternalValue<sizeof(T)>(reinterpret_cast<typename Base::TValue *>(&v));
swapDataBits(v, ShouldSwap<typename Base::TConfig>{});
}
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);
static_cast<Base*>(this)->readInternalBuffer(reinterpret_cast<typename Base::TValue *>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Base::TConfig>{});
}
template<typename T>
@@ -277,23 +337,27 @@ namespace bitsery {
private:
template<typename T>
void directRead(T *v, size_t count) {
static_assert(!std::is_const<T>::value, "");
static_cast<Base*>(this)->readInternal(reinterpret_cast<typename Base::TValue *>(v), sizeof(T) * count);
//swap each byte if necessary
_swapDataBits(v, count, ShouldSwap<typename Base::TConfig>{});
void swapDataBits(T *v, size_t count, std::true_type) {
using diff_t = typename std::iterator_traits<T*>::difference_type;
std::for_each(v, std::next(v, static_cast<diff_t>(count)), [](T &x) { x = details::swap(x); });
}
template<typename T>
void _swapDataBits(T *v, size_t count, std::true_type) {
std::for_each(v, std::next(v, count), [](T &x) { x = details::swap(x); });
}
template<typename T>
void _swapDataBits(T *, size_t , std::false_type) {
void swapDataBits(T *, size_t , std::false_type) {
//empty function because no swap is required
}
template<typename T>
void swapDataBits(T &v, std::true_type) {
v = details::swap(v);
}
template<typename T>
void swapDataBits(T &, std::false_type) {
//empty function because no swap is required
}
};
}

View File

@@ -91,18 +91,19 @@ namespace bitsery {
template<typename T>
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
return (v << 1) ^ (v >> (BitsSize<T>::value - 1));
return static_cast<SameSizeUnsigned<T>>((v << 1) ^ (v >> (BitsSize<T>::value - 1)));
}
template<typename TResult, typename TUnsigned>
TResult zigZagDecode(TUnsigned v, std::true_type) const {
return (v >> 1) ^ (~(v & 1) + 1); // same as -(v & 1), but no warning on VisualStudio
return static_cast<TResult>((v >> 1) ^ (~(v & 1) + 1)); // same as -(v & 1), but no warning on VisualStudio
}
// write/read bytes one by one
template<typename Writer, typename T>
void writeBytes(Writer &w, const T &v) const {
auto val = v;
using TFast = typename FastType<T>::type;
auto val= static_cast<TFast>(v);
while(val > 0x7Fu) {
w.template writeBytes<1>(static_cast<uint8_t>(val | 0x80u));
val >>=7u;
@@ -112,13 +113,16 @@ namespace bitsery {
template<bool CheckErrors, typename Reader, typename T>
void readBytes(Reader &r, T &v) const {
using TFast = typename FastType<T>::type;
constexpr auto TBITS = sizeof(T)*8;
uint8_t b1{0x80u};
auto i = 0u;
TFast tmp={};
for (;i < TBITS && b1 > 0x7Fu; i +=7u) {
r.template readBytes<1>(b1);
v += static_cast<T>(b1 & 0x7Fu) << i;
tmp += static_cast<TFast>(b1 & 0x7Fu) << i;
}
v = static_cast<T>(tmp);
handleReadOverflow<Reader, T>(r, i, b1,
std::integral_constant<bool, CheckOverflow && CheckErrors>{});
}

View File

@@ -74,8 +74,10 @@ namespace bitsery {
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
if (_alignBeforeData)
d.adapter().align();
if (index)
obj = *std::next(std::begin(_values), index-1);
if (index) {
using TDiff = typename std::iterator_traits<decltype(std::begin(_values))>::difference_type;
obj = static_cast<T>(*std::next(std::begin(_values), static_cast<TDiff>(index-1)));
}
else
fnc(d, obj);
}

View File

@@ -25,7 +25,7 @@
#include <unordered_set>
#include "../traits/core/traits.h"
#include "bitsery/ext/utils/memory_resource.h"
#include "../ext/utils/memory_resource.h"
namespace bitsery {

View File

@@ -0,0 +1,65 @@
//MIT License
//
//Copyright (c) 2020 Nick Renieris
//
//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_ATOMIC_H
#define BITSERY_EXT_STD_ATOMIC_H
#include "../traits/core/traits.h"
#include <atomic>
namespace bitsery {
namespace ext {
class StdAtomic {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser& ser, const std::atomic<T>& obj, Fnc&& fnc) const {
auto res = obj.load();
fnc(ser, res);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des& des, std::atomic<T>& obj, Fnc&& fnc) const {
T res{};
fnc(des, res);
obj.store(res);
}
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdAtomic, std::atomic<T>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_ATOMIC_H

View File

@@ -62,8 +62,8 @@ namespace bitsery {
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
auto key{bitsery::Access::create<TKey>()};
auto value{bitsery::Access::create<TValue>()};
auto key = bitsery::Access::create<TKey>();
auto value = bitsery::Access::create<TValue>();
fnc(des, key, value);
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
}

View File

@@ -58,7 +58,7 @@ namespace bitsery {
reserve(obj, size);
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
auto key{bitsery::Access::create<TKey>()};
auto key = bitsery::Access::create<TKey>();
fnc(des, key);
hint = obj.emplace_hint(hint, std::move(key));
}

View File

@@ -45,7 +45,7 @@ namespace bitsery {
};
// default implementation for MemResourceBase using new and delete
class MemResourceNewDelete : public MemResourceBase {
class MemResourceNewDelete final: public MemResourceBase {
public:
inline void* allocate(size_t bytes, size_t /*alignment*/, size_t /*typeId*/) final {
return (::operator new(bytes));
@@ -124,7 +124,8 @@ namespace bitsery {
// it just wraps our PolyAllocWithTypeId and pass 0 as typeId
// and defines core functions for c++ Allocator concept,
template<class T>
struct StdPolyAlloc {
class StdPolyAlloc {
public:
using value_type = T;
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)

View File

@@ -71,7 +71,7 @@ namespace bitsery {
StdPolyAlloc<PointerSharedStateBase> alloc{_memResource};
alloc.deallocate(data, 1);
}
MemResourceBase* _memResource;
MemResourceBase* _memResource = nullptr;
};
//PLC info is internal classes for serializer, and deserializer
@@ -153,7 +153,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
_ptrMap{StdPolyAlloc<std::pair<const void*, PLCInfoSerializer>>{memResource}} {}
_ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
@@ -190,7 +190,7 @@ namespace bitsery {
size_t _currId;
std::unordered_map<const void*, PLCInfoSerializer,
std::hash<const void*>, std::equal_to<const void*>,
StdPolyAlloc<std::pair<const void*, PLCInfoSerializer>>
StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>
> _ptrMap;
};
@@ -198,7 +198,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
_idMap{StdPolyAlloc<std::pair<size_t, PLCInfoDeserializer>>{memResource}} {}
_idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
@@ -244,7 +244,7 @@ namespace bitsery {
MemResourceBase* _memResource;
std::unordered_map<size_t, PLCInfoDeserializer,
std::hash<size_t>, std::equal_to<size_t>,
StdPolyAlloc<std::pair<size_t, PLCInfoDeserializer>>> _idMap;
StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>> _idMap;
};
}
@@ -257,7 +257,7 @@ namespace bitsery {
:pointer_utils::PointerLinkingContextSerialization(memResource),
pointer_utils::PointerLinkingContextDeserialization(memResource) {};
bool isValid() {
bool isValid() const {
return isPointerSerializationValid() && isPointerDeserializationValid();
}
};

View File

@@ -144,11 +144,10 @@ namespace bitsery {
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TDerived>()};
pointer_utils::StdPolyAlloc<THandler> alloc{_memResource};
auto ptr = alloc.allocate(1);
std::shared_ptr<THandler> handler(new (ptr)THandler{}, [this](THandler* data) {
std::shared_ptr<THandler> handler(new (ptr)THandler{}, [alloc](THandler* data) mutable {
data->~THandler();
pointer_utils::StdPolyAlloc<THandler> alloc{_memResource};
alloc.deallocate(data, 1);
}, pointer_utils::StdPolyAlloc<THandler>(_memResource));
}, alloc);
if (_baseToDerivedMap
.emplace(key, std::move(handler))
.second) {

View File

@@ -121,8 +121,8 @@ namespace bitsery {
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);
const T ratio = (v - r.min) / (r.max - r.min);
return static_cast<VT>(ratio * static_cast<T>(maxUint));
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
@@ -141,7 +141,7 @@ namespace bitsery {
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);
v = r.min + (static_cast<T>(intRep) / static_cast<T>(maxUint)) * (r.max - r.min);
}
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type * = nullptr>

View File

@@ -120,8 +120,8 @@ namespace bitsery {
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;
T value = v;
size_t bitsLeft = size;
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, valueSize);
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
@@ -132,7 +132,7 @@ namespace bitsery {
_scratch >>= valueSize;
_scratchBits -= valueSize;
value >>= valueSize;
value = static_cast<T>(value >> valueSize);
}
bitsLeft -= bits;
}
@@ -452,11 +452,12 @@ 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);
const size_t length = traits::TextTraits<T>::length(str);
assert((length + (traits::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
details::writeSize(this->_adapter, length);
auto begin = std::begin(str);
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
using diff_t = typename std::iterator_traits<decltype(begin)>::difference_type;
procContainer<VSIZE>(begin, std::next(begin, static_cast<diff_t>(length)), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
//process object types

View File

@@ -56,6 +56,8 @@ namespace bitsery {
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
using diff_t = typename T::difference_type;
static void resizeImpl(T& container, size_t size, std::true_type) {
container.resize(size);
}
@@ -65,7 +67,7 @@ namespace bitsery {
container.push_back(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
container.erase(std::next(std::begin(container), newSize));
container.erase(std::next(std::begin(container), static_cast<diff_t>(newSize)), std::end(container));
}
}
@@ -85,7 +87,7 @@ namespace bitsery {
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);
auto newSize = static_cast<size_t>(static_cast<double>(container.size()) * 1.5) + 128;
//make data cache friendly
newSize -= newSize % 64;//64 is cache line size
container.resize((std::max)(newSize, container.capacity()));

View File

@@ -44,6 +44,7 @@ namespace bitsery {
resizeImpl(container, size, std::is_default_constructible<TValue>{});
}
private:
using diff_t = typename std::forward_list<T, Allocator>::difference_type;
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t size, std::true_type) {
container.resize(size);
}
@@ -55,7 +56,7 @@ namespace bitsery {
if (oldSize > newSize) {
//erase_after must have atleast one element to work
if (newSize > 0)
container.erase_after(std::next(std::begin(container), newSize-1));
container.erase_after(std::next(std::begin(container), static_cast<diff_t>(newSize-1)));
else
container.clear();
}

11
patches/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Compiler specific patches
This folder will provide patches for various C++ compilers that are not C++11 compatible yet. This allows providing any fix for any compiler, without polluting core library with compiler-specific fixes.
A patch can be applied either with `git apply` or `patch` command, like this:
```bash
git apply patches/<patch_name>
patch -p1 < patches/<patch_name>
```
* [centos7_gcc4.8.2.diff](centos7_gcc4.8.2.diff) in this version, unordered_map is not fully C++11 compatible yet. It is lacking some constructors that accept allocator, and isn't using `std::allocator_traits`.

View File

@@ -0,0 +1,119 @@
diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h
index 6d5a441..462cee2 100644
--- a/include/bitsery/details/serialization_common.h
+++ b/include/bitsery/details/serialization_common.h
@@ -380,7 +380,7 @@ namespace bitsery {
template <typename ... TArgs>
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
: _adapter{std::forward<TArgs>(args)...},
- _context{ctx}
+ _context(ctx)
{
}
diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h
index f4c6655..5cd44ab 100644
--- a/include/bitsery/ext/inheritance.h
+++ b/include/bitsery/ext/inheritance.h
@@ -36,7 +36,7 @@ namespace bitsery {
class InheritanceContext {
public:
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
- :_virtualBases{pointer_utils::StdPolyAlloc<const void*>{memResource}}
+ :_virtualBases{0, std::hash<const void*>{}, std::equal_to<const void*>{}, pointer_utils::StdPolyAlloc<const void*>{memResource}}
{}
InheritanceContext(const InheritanceContext&) = delete;
InheritanceContext&operator = (const InheritanceContext&) = delete;
diff --git a/include/bitsery/ext/utils/memory_resource.h b/include/bitsery/ext/utils/memory_resource.h
index 472965a..18b3f31 100644
--- a/include/bitsery/ext/utils/memory_resource.h
+++ b/include/bitsery/ext/utils/memory_resource.h
@@ -24,6 +24,7 @@
#define BITSERY_EXT_MEMORY_RESOURCE_H
#include "../../details/serialization_common.h"
+#include <cstddef>
#include <new>
namespace bitsery {
@@ -128,6 +129,40 @@ namespace bitsery {
public:
using value_type = T;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ size_t max_size() const noexcept {
+ return std::numeric_limits<size_t>::max() / sizeof(value_type);
+ }
+
+ void construct(T *p, const T &val) {
+ new((void *) p) T(val);
+ }
+
+ template<class U, class... Args>
+ void construct(U *p, Args &&... args) {
+ new((void *) p) U(std::forward<Args>(args)...);
+ }
+
+ void destroy(T *p) {
+ p->~T();
+ }
+
+ template<class U>
+ void destroy(U *p) {
+ p->~U();
+ }
+
+ template<typename U>
+ struct rebind {
+ using other = StdPolyAlloc<U>;
+ };
+
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
:_alloc{memResource} {}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h
index f6f90da..6b65600 100644
--- a/include/bitsery/ext/utils/pointer_utils.h
+++ b/include/bitsery/ext/utils/pointer_utils.h
@@ -153,7 +153,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
- _ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
+ _ptrMap{0, std::hash<const void*>{}, std::equal_to<const void*>{}, StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
@@ -198,7 +198,7 @@ namespace bitsery {
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
- _idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
+ _idMap{0, std::hash<size_t>{}, std::equal_to<size_t>{}, StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h
index 6678230..a2cef4d 100644
--- a/include/bitsery/ext/utils/polymorphism_utils.h
+++ b/include/bitsery/ext/utils/polymorphism_utils.h
@@ -185,11 +185,8 @@ namespace bitsery {
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
:_memResource{memResource},
- _baseToDerivedMap{pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey,
- std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
- _baseToDerivedArray{pointer_utils::StdPolyAlloc<std::pair<const size_t,
- std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}}
- {}
+ _baseToDerivedMap{0, BaseToDerivedKeyHashier{}, std::equal_to<BaseToDerivedKey>{}, pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
+ _baseToDerivedArray{0, std::hash<size_t>{}, std::equal_to<size_t>{}, pointer_utils::StdPolyAlloc<std::pair<const size_t, std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}} {}
PolymorphicContext(const PolymorphicContext& ) = delete;
PolymorphicContext& operator = (const PolymorphicContext&) = delete;

View File

@@ -23,7 +23,7 @@
cmake_minimum_required(VERSION 3.10)
project(bitsery_tests CXX)
find_package(GTest 1.8 REQUIRED)
find_package(GTest 1.10 REQUIRED)
if (NOT TARGET Bitsery::bitsery)
message(FATAL_ERROR "Bitsery::bitsery alias not set. Please generate CMake from bitsery root directory.")
@@ -39,7 +39,10 @@ foreach (TestFile ${TestSourceFiles})
add_executable(${TestName} ${TestFile})
target_link_libraries(${TestName} PRIVATE GTest::Main Bitsery::bitsery)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++ -Wno-c++14-extensions)
target_compile_options(${TestName} PRIVATE -Wextra -Wconversion -Wno-missing-braces -Wpedantic -Weffc++ -Werror)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${TestName} PRIVATE -Wno-c++14-extensions)
endif()
gtest_discover_tests(${TestName})

View File

@@ -286,7 +286,7 @@ template <typename TConfig>
class InputAll: public AdapterConfig<TConfig> {
};
TYPED_TEST_CASE(InputAll, AdapterInputTypes);
TYPED_TEST_SUITE(InputAll, AdapterInputTypes,);
TYPED_TEST(InputAll, SettingMultipleErrorsAlwaysReturnsFirstError) {
@@ -455,7 +455,7 @@ template <typename TConfig>
class OutputAll: public AdapterConfig<TConfig> {
};
TYPED_TEST_CASE(OutputAll, AdapterOutputTypes);
TYPED_TEST_SUITE(OutputAll, AdapterOutputTypes,);
TYPED_TEST(OutputAll, CanBeMoveConstructedAndMoveAssigned) {
auto w = this->config.createWriter();
@@ -500,15 +500,15 @@ using BufferedAdapterInternalBufferTypes = ::testing::Types<
std::string
>;
TYPED_TEST_CASE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes);
TYPED_TEST_SUITE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes,);
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferAndRemainingDataToStream) {
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferToStream) {
uint8_t x{};
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
this->writer.template writeBytes<1>(x);
EXPECT_TRUE(this->stream.str().empty());
this->writer.template writeBytes<1>(x);
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize));
}
TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) {

View File

@@ -22,6 +22,7 @@
#include <bitsery/brief_syntax.h>
#include <bitsery/brief_syntax/array.h>
#include <bitsery/brief_syntax/atomic.h>
#include <bitsery/brief_syntax/chrono.h>
#include <bitsery/brief_syntax/deque.h>
#include <bitsery/brief_syntax/forward_list.h>
@@ -39,16 +40,19 @@
#include <bitsery/brief_syntax/tuple.h>
#include <bitsery/brief_syntax/variant.h>
#else
#if defined(_MSC_VER)
/*#if defined(_MSC_VER)
#pragma message("tuple and variant only works with c++17")
#else
#warning "tuple and variant only works with c++17"
#endif
#endif*/
#endif
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <atomic>
#include <utility>
using testing::Eq;
TEST(BriefSyntax, FundamentalTypesAndBool) {
@@ -151,6 +155,14 @@ T procBriefSyntax(const T& testData) {
return res;
}
template<typename T>
T&& procBriefSyntaxRvalue(T&& init_value, const T& testData) {
SerializationContext ctx;
ctx.createSerializer()(testData);
ctx.createDeserializer()(init_value);
return std::move(init_value);
}
template<typename T>
T procBriefSyntaxWithMaxSize(const T& testData) {
SerializationContext ctx;
@@ -411,6 +423,21 @@ TEST(BriefSyntax, StdTimePoint) {
EXPECT_TRUE(procBriefSyntax(data) == data);
}
TEST(BriefSyntax, StdAtomic) {
std::atomic<int32_t> atm0{54654};
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<int32_t>{}, atm0) == atm0);
std::atomic<bool> atm1{false};
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<bool>{}, atm1) == atm1);
std::atomic<bool> atm2{true};
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<bool>{}, atm2) == atm2);
std::atomic<uint16_t> atm3;
atm3.store(0x1337);
EXPECT_TRUE(procBriefSyntaxRvalue(std::atomic<uint16_t>{}, atm3).load() == 0x1337);
}
#if __cplusplus > 201402L
TEST(BriefSyntax, StdTuple) {

View File

@@ -42,7 +42,7 @@ using FixedContainer = std::array<uint8_t, 100>;
using ContainerTypes = ::testing::Types<FixedContainer,NonFixedContainer>;
TYPED_TEST_CASE(DataWriting, ContainerTypes);
TYPED_TEST_SUITE(DataWriting, ContainerTypes,);
static constexpr size_t DATA_SIZE = 14u;

View File

@@ -96,6 +96,7 @@ TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
std::vector<NonDefaultConstructible> res{};
res.emplace_back(2);
res.emplace_back(3);
res.emplace_back(4);
ctx.createSerializer().container(data, 10);
ctx.createDeserializer().container(res, 10);

View File

@@ -96,7 +96,7 @@ using SequenceContainersWithArthmeticTypes = ::testing::Types<
std::forward_list<int>,
std::deque<unsigned short>>;
TYPED_TEST_CASE(SerializeContainerDynamicSizeArthmeticTypes, SequenceContainersWithArthmeticTypes);
TYPED_TEST_SUITE(SerializeContainerDynamicSizeArthmeticTypes, SequenceContainersWithArthmeticTypes,);
TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, Values) {
SerializationContext ctx{};
@@ -125,7 +125,7 @@ TYPED_TEST(SerializeContainerDynamicSizeArthmeticTypes, CustomFunctionIncrements
});
//decrement result by 1, before comparing for eq
for (auto &v:this->res)
v -= 1;
v = static_cast<TValue>(v-1);
EXPECT_THAT(ctx.getBufferSize(), Eq(this->getExpectedBufSize(ctx)));
EXPECT_THAT(this->res, ContainerEq(this->src));
@@ -151,7 +151,7 @@ using SerializeContainerDynamicSizeWithCompositeTypes = ::testing::Types<
std::vector<MyStruct1>,
std::list<MyStruct2>>;
TYPED_TEST_CASE(SerializeContainerDynamicSizeCompositeTypes, SerializeContainerDynamicSizeWithCompositeTypes);
TYPED_TEST_SUITE(SerializeContainerDynamicSizeCompositeTypes, SerializeContainerDynamicSizeWithCompositeTypes,);
TYPED_TEST(SerializeContainerDynamicSizeCompositeTypes, DefaultSerializeFunction) {
SerializationContext ctx{};
@@ -189,7 +189,7 @@ using StaticContainersWithIntegralTypes = ::testing::Types<
std::array<int16_t, 4>,
int16_t[4]>;
TYPED_TEST_CASE(SerializeContainerFixedSizeArithmeticTypes, StaticContainersWithIntegralTypes);
TYPED_TEST_SUITE(SerializeContainerFixedSizeArithmeticTypes, StaticContainersWithIntegralTypes,);
TYPED_TEST(SerializeContainerFixedSizeArithmeticTypes, ArithmeticValues) {
using Container = typename TestFixture::TContainer;
@@ -214,7 +214,7 @@ class SerializeContainerFixedSizeCompositeTypes : public SerializeContainerFixed
using StaticContainersWithCompositeTypes = ::testing::Types<
std::array<MyStruct1, 4>, MyStruct1[4]>;
TYPED_TEST_CASE(SerializeContainerFixedSizeCompositeTypes, StaticContainersWithCompositeTypes);
TYPED_TEST_SUITE(SerializeContainerFixedSizeCompositeTypes, StaticContainersWithCompositeTypes,);
TYPED_TEST(SerializeContainerFixedSizeCompositeTypes, DefaultSerializationFunction) {
using Container = typename TestFixture::TContainer;
@@ -269,6 +269,5 @@ TEST_P(SerializeContainer, SizeHasVariableLength) {
EXPECT_THAT(ctx.getBufferSize(), Eq(ctx.containerSizeSerializedBytesCount(src.size())));
}
//last comma is to suppress error that otherwise can be suppressed by clang/gcc with -Wgnu-zero-variadic-macro-arguments
INSTANTIATE_TEST_CASE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000),);
INSTANTIATE_TEST_SUITE_P(LargeContainerSize, SerializeContainer, ::testing::Values(0x01, 0x80, 0x4000));

View File

@@ -37,17 +37,17 @@ using bitsery::EndiannessType;
// helper function, that gets value filled with specified number of bits
template <typename TValue>
TValue getValue(bool isPositive, size_t significantBits) {
TValue v = isPositive ? 0 : -1;
TValue v = isPositive ? 0 : static_cast<TValue>(-1);
if (significantBits == 0)
return v;
using TUnsigned = typename std::make_unsigned<TValue>::type;
TUnsigned mask = {};
mask = ~mask; // invert shiftByBits
mask = static_cast<TUnsigned>(~mask); // invert shiftByBits
auto shiftBy = bitsery::details::BitsSize<TValue>::value - significantBits;
mask >>= shiftBy;
mask = static_cast<TUnsigned>(mask >> shiftBy);
//cast to unsigned when applying mask
return (TUnsigned)v ^ mask;
return v ^ static_cast<TValue>(mask);
}
// helper function, that serialize and return deserialized value
@@ -118,7 +118,7 @@ using AllValueSizesTestCases = ::testing::Types<
TC<int64_t, false, BigEndianConfig>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases);
TYPED_TEST_SUITE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases,);
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
using TCase = typename TestFixture::TestCase;
@@ -202,7 +202,7 @@ using RequiredBytesTestCases = ::testing::Types<
SizeTC<int64_t, false, 63,10>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases);
TYPED_TEST_SUITE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases,);
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
using TCase = typename TestFixture::TestCase;

View File

@@ -176,7 +176,7 @@ TEST(SerializeExtensionEntropy, CustomFunctionNotEntropyEncodedWithAlignBeforeDa
});
EXPECT_THAT(res, Eq(v));
auto bitsForIndex = 8; //because aligned
auto bitsForIndex = 8u; //because aligned
EXPECT_THAT(ctx.getBufferSize(), Eq((bitsForIndex + rangeForValue.getRequiredBits() * 2 - 1) / 8 + 1 ));
}

View File

@@ -87,7 +87,7 @@ struct MultipleVirtualInheritance : Derived1, Derived2 {
MultipleVirtualInheritance() = default;
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, uint8_t z_) {
MultipleVirtualInheritance(uint8_t x_, uint8_t y1_, uint8_t y2_, int8_t z_) {
x = x_;
y1 = y1_;
y2 = y2_;

View File

@@ -89,7 +89,7 @@ using SerializeExtensionStdMapTypes = ::testing::Types<
std::multimap<int32_t ,int64_t>
>;
TYPED_TEST_CASE(SerializeExtensionStdMap, SerializeExtensionStdMapTypes);
TYPED_TEST_SUITE(SerializeExtensionStdMap, SerializeExtensionStdMapTypes,);
namespace bitsery {

View File

@@ -121,9 +121,9 @@ TEST(SerializeExtensionStdOptional, NoAlignAfterStateWriteRead) {
}
#else
#if defined(_MSC_VER)
/*#if defined(_MSC_VER)
#pragma message("Tests for StdOptional requires C++17")
#else
#warning "Tests for StdOptional requires C++17"
#endif
#endif*/
#endif

View File

@@ -44,7 +44,7 @@ using SerializeExtensionStdSetTypes = ::testing::Types<
std::set<int32_t>,
std::multiset<int32_t>>;
TYPED_TEST_CASE(SerializeExtensionStdSet, SerializeExtensionStdSetTypes);
TYPED_TEST_SUITE(SerializeExtensionStdSet, SerializeExtensionStdSetTypes,);
TYPED_TEST(SerializeExtensionStdSet, ValuesSyntaxDifferentSetTypes) {
SerializationContext ctx1;

View File

@@ -71,7 +71,7 @@ void serialize(S& s, Derived& o) {
}
struct MoreDerived : Derived {
int8_t z{};
uint8_t z{};
MoreDerived() = default;
@@ -197,13 +197,13 @@ using TestingWithNonPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes);
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrNonPolymorphicType, TestingWithNonPolymorphicTypes,);
using TestingWithPolymorphicTypes = ::testing::Types<
UniquePtrTest,
SharedPtrTest>;
TYPED_TEST_CASE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes);
TYPED_TEST_SUITE(SerializeExtensionStdSmartPtrPolymorphicType, TestingWithPolymorphicTypes,);
TYPED_TEST(SerializeExtensionStdSmartPtrNonPolymorphicType, Data0Result0) {
using Ptr = typename TestFixture::template TPtr<MyStruct1>;

View File

@@ -119,9 +119,9 @@ TEST(SerializeExtensionStdTuple, NonDefaultConstructable) {
}
#else
#if defined(_MSC_VER)
/*#if defined(_MSC_VER)
#pragma message("Tests for StdTuple requires C++17")
#else
#warning "Tests for StdTuple requires C++17"
#endif
#endif*/
#endif

View File

@@ -168,9 +168,9 @@ TEST(SerializeExtensionStdVariant, CorrectlyHandleMonoState) {
}
#else
#if defined(_MSC_VER)
/*#if defined(_MSC_VER)
#pragma message("Tests for StdVariant requires C++17")
#else
#warning "Tests for StdVariant requires C++17"
#endif
#endif*/
#endif