86 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
Mindaugas Vinkelis
f85dff7415 few more renames and updated change log for version 5.0.0 2019-07-09 07:02:44 +03:00
Mindaugas Vinkelis
f35ae3f4dc renamed flexible to brief_syntax 2019-07-05 14:18:27 +03:00
Mindaugas Vinkelis
ff40222124 removed Reader/Writer parameter from extensions serialize/deserialize
methods
2019-07-05 14:18:21 +03:00
Mindaugas Vinkelis
a1785f3e15 all bitsery types that allocates memory can be configured to accept
memory resource for allocation
2019-07-05 14:17:38 +03:00
Mindaugas Vinkelis
105aa5f9e5 simplified usage by merging adapter writer/reader with input/output
adapter and ability to disable checks on deserialization
2019-07-02 08:03:34 +03:00
Mindaugas Vinkelis
1822796f2e multiple breaking change improvements 2019-06-27 11:40:57 +03:00
Mindaugas Vinkelis
57dd028b7a added custom allocator support for pointer like objects. 2019-06-27 11:30:34 +03:00
Mindaugas Vinkelis
03f2c3c8b5 bugfix in enableBitPacking 2019-06-27 10:08:48 +03:00
Luli2020
aca3139600 Added serializer/deserializer "cereal like" interface
Added interface BasicSerializer &operator()(T &&head, TArgs &&... tail) and BasicDeserializer &operator()(T &&head, TArgs &&... tail), which are cereal like intefaces so such format of serialization functions were possible:

template<class Archive>
void serialize(Archive & ar, DataType& d){
    ar(d.data1, d.data2....);
}
2019-06-27 08:07:57 +03:00
Mindaugas Vinkelis
c1ae593fb4 travis-ci-update 2019-06-27 08:08:05 +03:00
Mindaugas Vinkelis
ddca8e4ad0 extensions for tuple, variant and chrono types 2019-03-12 14:54:04 +02:00
Mindaugas Vinkelis
1fe2b398fc Merge pull request #12 from ArekSredzki/fix-quickMeasureSize
Fix quickMeasureSize<T>() compilation.
2019-01-30 11:49:37 +02:00
Arek Sredzki
574ec69cca Fix quickMeasureSize<T>() compilation. 2019-01-30 01:44:34 -08:00
Mindaugas Vinkelis
8e94596a6f Merge pull request #11 from ArekSredzki/misc-documentation-improvements
Fix various grammatical and spelling mistakes within the docs
2019-01-18 14:37:59 +02:00
Arek Sredzki
fac2c8a7ce Fix various grammatical and spelling mistakes within the docs 2019-01-17 10:31:58 -08:00
Mindaugas
a6dad0885f visual studio variadic templates issues 2019-01-16 11:27:56 +02:00
Mindaugas
65f90637df input buffer adapter accepts const buffer 2019-01-10 20:48:03 +02:00
Mindaugas
b10f86da00 non default constructible types 2019-01-10 19:08:15 +02:00
Mindaugas
6c3e1aee43 removed anonymous namespace from PolymorphicBaseClass as it only works
on clang, and is not standard compliant
2019-01-08 16:00:02 +02:00
Mindaugas
e5f8d5742f Merge branch 'master' of https://github.com/fraillt/bitsery 2019-01-08 15:08:27 +02:00
Mindaugas
a2ecf8d7b0 polymorphism improvements and new CompactValue extension 2019-01-08 15:06:29 +02:00
Mindaugas Vinkelis
670130397b Merge pull request #6 from AJIOB/master
VS 2017.5.6 example project compilation fix
2018-09-17 09:47:36 +03:00
AJIOB
4a0b3cae98 VS 2017.5.6 example compilation fix 2018-09-15 08:37:56 +03:00
Mindaugas Vinkelis
b3b32ab393 Merge pull request #5 from YarikTH/patch-1
Update smart_pointers_with_polymorphism.cpp
2018-08-27 07:24:22 +03:00
Yaroslav
6ebdb9915b Update smart_pointers_with_polymorphism.cpp
Fix Color::operator == in smart_pointers_with_polymorphism example
2018-08-24 10:09:17 +03:00
Mindaugas
2e62bd08e3 cleanup 2018-08-23 14:57:48 +03:00
Mindaugas
54f69a5eea polymorphism and smart pointers 2018-08-23 14:44:58 +03:00
Mindaugas Vinkelis
275c4138ee polymorphism in progress 2018-08-20 13:10:10 +03:00
Mindaugas Vinkelis
1ca45aab79 updated travis script 2018-03-09 22:03:14 +02:00
fraillt
952635ff70 improved configuration to follow modern cmake practices 2018-03-09 15:59:28 +02:00
fraillt
507b5ae01d added polymorphism support for raw pointers 2018-02-28 16:06:03 +02:00
fraillt
f6d02aba38 added inheritance extension, and ability to have internal contexts within serializer/deserializer 2017-11-12 12:09:12 +02:00
fraillt
be9ccf08d9 added NotNull pointer to to pointer extensions 2017-10-30 09:01:08 +02:00
fraillt
5b1dc3bcfa raw pointers support, without polymorphism. 2017-10-27 08:14:01 +03:00
fraillt
bdc24eb3c2 compile warnings, and usage improvements for VisualStudio 2017-10-18 11:43:53 +03:00
fraillt
1acb9af188 added bitpacking and context usage examples 2017-10-16 07:52:00 +03:00
fraillt
4ccd4d368e maxSize archive modifying function now works with extensions 2017-10-13 16:04:02 +03:00
fraillt
f3c9a33849 new stream adapter, and lots of refactorings 2017-10-13 07:26:34 +03:00
fraillt
5ede853954 added adapter type for serializer/deserializer 2017-10-06 12:54:43 +03:00
fraillt
982374be42 added more std containers 2017-10-04 10:10:59 +03:00
fraillt
ab5cc8a2c0 flexible syntax 2017-10-02 06:55:01 +03:00
fraillt
f0508025f6 separated advanced features from core functionality 2017-09-21 16:18:23 +03:00
fraillt
ad7090539e refactoring to work with C++11, and added containerMap extension 2017-09-19 11:13:59 +03:00
fraillt
2f8ae0075c added -growable- function 2017-09-15 15:56:33 +03:00
Mindaugas Vinkelis
cba80774e4 fixed some errors before merging to master 2017-09-03 19:34:08 +03:00
fraillt
241f1f2940 buffer reading/writing improvements 2017-09-01 16:23:52 +03:00
fraillt
fe2bccf28f Merge branch 'master' into documentation 2017-08-28 09:59:56 +03:00
fraillt
944197db97 buffer writer improvements 2017-08-28 09:59:03 +03:00
Mindaugas Vinkelis
a852174329 Update README.md 2017-08-28 09:58:12 +03:00
Mindaugas Vinkelis
fb89af8112 Merge pull request #1 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2017-08-28 09:49:33 +03:00
The Gitter Badger
4df4df9317 Add Gitter badge 2017-08-28 06:38:09 +00:00
fraillt
5d192f2002 Merge branch 'documentation' of http://github.com/fraillt/bitsery into documentation 2017-08-24 15:04:31 +03:00
Mindaugas Vinkelis
79ee168893 turorial 2in1 2017-08-24 14:58:27 +03:00
Mindaugas Vinkelis
5c696f14bd turorial 2in1 2017-08-21 20:49:34 +03:00
Mindaugas Vinkelis
a1104a9b95 hello world tutorial complete 2017-08-14 22:43:51 +03:00
Mindaugas Vinkelis
6e8898317e Merge branch 'master' into documentation 2017-08-13 16:41:33 +03:00
Mindaugas Vinkelis
19555528bb added travis build status 2017-08-12 10:26:21 +03:00
fraillt
1986491173 ext function redesign and documentation 2017-08-11 16:11:55 +03:00
fraillt
4ad65f8384 added endianness support 2017-07-25 10:34:07 +03:00
fraillt
014b072b96 added extension support, and changed invoke parameters for custom serialization function 2017-06-20 11:35:12 +03:00
fraillt
1176f4908e enforce type size on serializer/deserializer functions that accept primitive types 2017-05-30 15:58:50 +03:00
fraillt
6ab4378bc2 renaming source files 2017-02-23 15:22:31 +02:00
fraillt
bf712bc9e5 added more BufferReader ctors 2017-02-23 10:25:33 +02:00
144 changed files with 17039 additions and 3592 deletions

5
.gitignore vendored
View File

@@ -1,3 +1,6 @@
.idea/
.vs/
build/
cmake-build-debug/
cmake-build-*
CTestConfig.cmake
Testing/

70
.travis.yml Normal file
View File

@@ -0,0 +1,70 @@
dist: xenial
language: cpp
# CXX_COMPILER and CC_COMPILER is defined, because travis will override CC and CXX environment variables
# We'll need to override them back "before_install"
matrix:
include:
- addons:
apt:
packages:
- g++-5
env:
- CXXSTD=11
- CXX_COMPILER=g++-5
- CC_COMPILER=gcc-5
- addons:
apt:
packages:
- clang-3.9
env:
- CXXSTD=11
- CXX_COMPILER=clang++-3.9
- CC_COMPILER=clang-3.9
- addons:
apt:
packages:
- g++-7
sources:
- ubuntu-toolchain-r-test
env:
- CXXSTD=17
- CXX_COMPILER=g++-7
- CC_COMPILER=gcc-7
- addons:
apt:
packages:
- libstdc++-7-dev
- clang-8
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-xenial-8
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
env:
- CXXSTD=17
- CXX_COMPILER=clang++-8
- CC_COMPILER=clang-8
before_install:
- export CXX=$CXX_COMPILER
- export CC=$CC_COMPILER
install:
- 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
- cd ..
before_script:
- mkdir build
- cd build
  - cmake -DBITSERY_BUILD_TESTS=ON -DCMAKE_CXX_STANDARD=$CXXSTD ..
script:
- make
- cd tests
- ctest

396
CHANGELOG.md Normal file
View File

@@ -0,0 +1,396 @@
# [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.
Additionally, it provides more customization options for serialization/deserialization flow.
### Features
* added config options to enable/disable checking input adapter and data errors: **CheckAdapterErrors** and **CheckDataErrors**.
If you can trust your data this will improve deserialization performance. Error checks are enabled by default.
* all memory allocation in the library can be customized using memory resource (similar to c++ 17 memory resource)
* contexts that internally requires to allocate memory are: *PointerLinkingContext, PolymorphicContext, and InheritanceContext*.
* extensions, for pointers like types, that allocate memory are: *PointerOwner* and *StdSharedPtr*.
More information about pointer extension can be found [here](doc/design/pointers.md).
### Breaking changes
* improved design for serializer/deserializer *context*. It was hard to understand and easy to misuse, so several changes were made.
* removed *internal* context from config, because it doesn't actually solve any problems, only allows doing the same thing in multiple ways.
* removed `T* context()`. This allowed to get a context that is `std::tuple<...>`, but you can do the same with other methods, by wrapping in an outer tuple, e.g. `std::tuple<std::tuple<...>>`.
* if a context is defined, in serializer/deserializer, it is passed (and stored) by reference as a first argument (instead of pointer). Other parameters are forwarded to a input/output adapter.
* changed signature `T* context<T>` to `T& context<T>`, this will either return context or doesn't compile, previously it could also return nullptr.
* `context<T>` and `contextOrNull<T>` now also check if a type is convertible, so it can work with base classes
(e.g. you can require a base class of context in extension, but provided child implementation instead). This allows to further customise extension by providing different context implementations.
* reworked *BufferedSessions*. It was the only feature that was in a bitsery core and required allocations.
It was removed and instead was added ability to change read/write position directly for buffered adapters.
This is also a more flexible solution, and can be used to implement solutions that allow deserialization [in-place](https://github.com/fraillt/bitsery/issues/19) like *flatbuffers* does.
* changed the signature to all serialization functions that accept lambdas, instead of accepting `(T& )`, now accept `(S& ,T& )`.
This allows much easier serialization customization, because no additional state is required in a functor, and these methods can now accept function pointers or stateless lambdas.
* removed various functions/classes that became redundant:
* *AdapterWriter/Reader* classes - their functionality is moved to *adapters*.
* *AdapterAccess* class - now serializer/deserializer expose adapter directly via `adapter()` method.
Additionally adapter can be moved out if serializer/deserializer is rvalue .e.g `auto adapter = std::move(ser).adapter();`.
* removed *Writer/Reader* parameters from extensions *serialize/deserialize* methods - because serializer/deserializer now expose *adapter* directly.
* *archive* from serializer/deserializer - because `operator()` do the same thing, but it is more terse, and is compatible with `cereal` library.
* *align* function from serializer/deserializer - it can now be called directly on input/output adapter.
* *UnsafeInputBufferAdapter* - instead config option is provided to disable adapter read errors (*CheckAdapterErrors*).
* *isValidState* from stream output adapter - it didn't provide any additional information that couldn't be queried directly on stream object.
* *registerBasesList* from *PolymorphicContext* - it was previously deprecated.
* renamed various classes/functions and files:
* *flexible* to **brief_syntax** - this is a big change and might affect a lot of code, but it was necessary, because this name was contradicting to what it actually was trying to achieve.
Migration should not be very hard, just global replace `flexible` to `brief_syntax` and it should work:).
* *BasicSerializer/BasicDeserializer* to **Serializer/Deserializer** - previously they were aliases, but after removing adapter writer/reader,
serializer/deserializer signature has changed to `Serializer/Deserializer<Adapter, Context=void>` and no longer need any alias.
* *setError* to **error** for input adapters.
* *NetworkEndianness* to **Endianness** in config.
* *MeasureSize* adapter moved to separate file `/adapter/measure_size.h`
### Improvements
* added `quickSerialization/Deserialization` overloads that can accept context as first parameter.
* added convenience classes (functors) **FtorExtValue, FtorExtObject**, in places where you need to provide (des)serialize function/lambda that uses extension.
e.g. instead of writing `s.container(obj, [](S& s, MyData& data) {s.ext(data, MyExtension{});});` you can write `s.container(obj, FtorExtObject<MyExtension>{});`
* added more tests for adapters.
* input adapters now save first error that occured, this allows better diagnostic for what actually happened.
### Bug fixes
* fixed `enabledBitPacking` in serializer/deserializer where writer/reader and internal context states were not restored properly after bit packing is complete.
# [4.6.1](https://github.com/fraillt/bitsery/compare/v4.6.0...v4.6.1) (2019-06-27)
### Features
* flexible syntax now also supports `cereal` like serialization interface (thanks to [Luli2020](https://github.com/Luli2020))
### Bug fixes
* when using `enableBitPacking`, internal context (i.e. context<T>()) in serializer/deserializer was not copied to/from new bitpacking enabled instance.
# [4.6.0](https://github.com/fraillt/bitsery/compare/v4.5.1...v4.6.0) (2019-03-12)
### Features
* new extensions **StdTuple** and **StdVariant** for `std::tuple` and `std::variant`. These are the first extensions that requires C++17, or higher, standard enabled.
Although `std::tuple` is C++11 type, but from usage perspective it has exactly the same requirements as `std::variant` and relies heavily on having class template argument deduction guides to make it convenient to use.
You can easily use `std::tuple` without any extension at all, so the main motivation was to create convenient interface for **StdVariant** and use the same interface for **StdTuple** as well.
* instead of providing custom lambda to overload each type in tuple or variant, there was added several helper callable objects.
**OverloadValue** wrapper around `s.value<N>(o)`, **OverloadExtValue** wrapper around `s.ext<N>(o, Ext{})` and **OverloadExtObject** wrapper around `s.ext(o, Ext{})`.
* new extensions **StdDuration** and **StdTimePoint** for `std::chrono::duration` and `std::chrono::time_point`.
### Improvements
tests now uses `gtest_discover_tests` function, to automatically discover tests, which requires CMake 3.10.
# [4.5.1](https://github.com/fraillt/bitsery/compare/v4.5.0...v4.5.1) (2019-01-16)
### Improvements
* template specializations, where possible, was changed to avoid using variadics, some Visual Studio compilers has [issues](https://developercommunity.visualstudio.com/content/problem/3437/error-with-c11-variadics.html) with variadic templates.
* reduced compile warnings for VisualStudio:
* added explicit casts
* renamed `struct` to `class` where class is used as friend. e.g. `friend class bitsery::Access`, because it is more conventional usage.
# [4.5.0](https://github.com/fraillt/bitsery/compare/v4.4.0...v4.5.0) (2019-01-10)
### Features
* ability to create non default constructible objects, by defining private default constructor and making `friend class bitsery::Access;` to access it.
It is not necessary to enforce class invariant immediately, because internal object representation will be overriden anyway.
### Improvements
* `StdSmartPtr` supports `std::unique_ptr` with custom deleter.
* `*InputBufferAdapter`(all) can also accept const buffer;
### Bug fixes
* fixed deserialization in `bitsery/ext/std_map{set}` when target container is not empty.
* added missing template parameters for specializations on `std` containers in multiple files in `bitsery/ext/*`.
# [4.4.0](https://github.com/fraillt/bitsery/compare/v4.3.0...v4.4.0) (2019-01-08)
### Features
* new extensions **CompactValue** and **CompactValueAsObject**, stores integral values in less space if possible. This is useful when you're working with mostly small values, that in rare cases can be large.
E.g. `int64_t money = 8000;` will only use 2 bytes, instead of 8. **CompactValueAsObject** allows to use `ext()` overload, without specifying size of underlying type and sets BUFFER_OVERFLOW error if value doesn't fit in underlying type during deserialization.
### Improvements
* improved **PolymorphicContext**, allows to extend already registered hierarchy in one translation unit, using different type other than `PolymorphicBaseClass` to avoid symbol collision between translation units or libraries.
`registerBasesList` was modified, so that it could accept user defined type (instead of `PolymorphicBaseClass`) that is used to declare hierarchy, by default it is `PolymorphicBaseClass`.
This introduced breaking change, for those who used this syntax (`registerBasesList<MySerializer, Shape>({})`) during registration.
It is encouraged to define helper type, that could be used for registering hierarchy for serialization and deserialization [example](examples/smart_pointers_with_polymorphism.cpp).
`This is only relevant then you want to use **PolymorphicContext** between different translation units or libraries`.
```cpp
//libA
namespace bitsery {
namespace ext {
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {};
}
}
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
...
ctx.registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{}).
//otherLib
struct MySquare: Shape {...}
//now it must define different type (exactly the same as PolymorphicBaseClass) to declare hierarchy
template<typename TBase>
struct MyHierarchy {
using Childs = PolymorphicClassesList<>;
};
template <>
struct MyHierarchy<Shape>: bitsery::ext::bitsery::ext::PolymorphicClassesList<MySquare> {};
...
//notice that we pass MyHierarchy as second argument
ctx.registerBasesList<MySerializer, MyHierarchy>(MyPolymorphicClassesForRegistering{}).
```
* **PolymorphicContext** also get optional method `registerSingleBaseBranch`, that allows manually register hierarchies, this might be more convenient when using you need to register in different translation units (or libraries), but it is error-prone.
# [4.3.0](https://github.com/fraillt/bitsery/compare/v4.2.1...v4.3.0) (2018-08-23)
### Features
* added runtime polymorphism support for pointer like types (raw and smart pointers).
In order to enable polymorphism new **PolymorphicContext** was created. It provides capability to register classes with serializer/deserializer.
* runtime polymorphism can be customized, by replacing **StandardRTTI** from <bitsery/ext/utils/rtti_utils.h> header.
* added smart pointers support for std::unique_ptr, std::shared_ptr and std::weak_ptr via **StdSmartPtr** extension.
* new **UnsafeInputBufferAdapter** doesn't check for buffer size on deserialization, on some compilers can improve deserialization performance up to ~40%.
### Improvements
* creatly improved interface for extending/implementing support for pointer like types. Now all pointer like types extends from **PointerObjectExtensionBase** and implements/configures required details.
* reimplemented **PointerOwner**, **PointerObserver**, **ReferencedByPointer**.
* reimplemented **PointerLinkingContext** to properly support shared objects and runtime polymorphism, pointer ownership for shared objects now has two states: SharedOwner e.g. std::shared_ptr and SharedObserver std::weak_ptr.
### Other notes
There is one *minor?* issue/limitation for pointer like types that uses virtual inheritance. When several pointers points to same object through different static type. it will not work correctly e.g.:
```cpp
struct Derived: virtual Base {...};
struct MyData {
std::shared_ptr<Derived> sptr;
std::weak_ptr<Base> wptddr;
}
```
In this example wptr and sptr have different static type, and *Derived* is virtually inherited from *Base*, so I get different pointer address for different types.
# [4.2.1](https://github.com/fraillt/bitsery/compare/v4.2.0...v4.2.1) (2018-03-09)
### Improvements
* changed CMake structure, to follow **Modern CMake** principles.
* bitsery now has *install* target and **find_package(Bitsery)** exports **Bitsery::bitsery** target.
* *GTest* no longer downloads as external application, but tries to find via *find_package*.
* removed *ext* folder, and instead added *scripts* folder that contains few helper scripts for development, currently tested on Ubuntu.
* fixed/added few tests cases.
### Other notes
* some work was done on polymorphism support, but current solution, although working, but has many design and performance issues, and interfaces for extensibility might change drastically.
# [4.2.0](https://github.com/fraillt/bitsery/compare/v4.1.0...v4.2.0) (2017-11-12)
### Features
* serializer/deserializer can now have **internal context(s)** via configuration.
It is convenient way to pass context, when it doesn't convey useful information outside of serializer/deserializer and is default constructable.
* added **contextOrNull\<T\>()** overload to *BasicSerializer/BasicDeserializer*.
Difference between *contextOrNull\<T\>()* and *context\<T\>()* is, that using *context\<T\>()* code doesn't compile if T doesn't exists at all, while using *contextOrNull\<T\>()* code compiles, but returns *nullptr* at runtime.
* added inheritance support via extensions.
In order to correctly manage virtual inheritance two extensions was created in **<bitsery/ext/inheritance.h>** header:
* **BaseClass\<TBase\>** - use when inheriting from objects without virtual inheritance.
* **VirtualBaseClass\<TBase\>** - ensures that only one copy of each virtual base class is serialized.
To keep track of virtual base classes **InheritanceContext** is required, but it is optional if no virtual bases exists in serialization flow.
I.e. if context is not defined, code will not compile only if virtual inheritance is used.
See [inheritance](examples/inheritance.cpp) for usage example.
### Improvements
* added optional ctor parameter for *PointerOwner* and *PointerObserver* - **PointerType**, which specifies if pointer can be null or not.
Default is **Nullable**.
# [4.1.0](https://github.com/fraillt/bitsery/compare/v4.0.1...v4.1.0) (2017-10-27)
### Features
* added raw pointers support via extensions.
In order to correctly manage pointer ownership, three extensions was created in **<bitsery/ext/pointer.h>** header:
* **PointerOwner** - manages life time of the pointer, creates or destroys if required.
* **PointerObserver** - doesn't own pointer so it doesn't create or destroy anything.
* **ReferencedByPointer** - when non-owning pointer (*PointerObserver*) points to reference type, this extension marks this object as a valid target for PointerObserver.
To validate and update pointers **PointerLinkingContext** have to be passed to serialization/deserialization.
It ensures that all pointers are valid, that same pointer doesn't have multiple owners, and non-owning pointers doesn't point outside of scope (i.e. non owning pointers points to data that is serialized/deserialized), see [raw_pointers example](examples/raw_pointers.cpp) for usage example.
*Currently polimorphism and std::shared_ptr, std::unique_ptr is not supported.*
* added **context\<T\>()** overload to *BasicSerializer/BasicDeserializer* and now they became typesafe.
For better extensions support, added posibility to have multiple types in context with *std::tuple*.
E.g. when using multiple extensions, that requires specific contexts, together with your custom context, you can define your context as *std::tuple\<PointerLinkingContext, MyContext\>* and in serialization function you can correctly get your data via *context\<MyContext\>()*.
### Improvements
* new **OutputBufferedStreamAdapter** use internal buffer instead of directly writing to stream, can get more than 2x performance increase.
* can use any contiguous container as internal buffer.
* when using fixed-size, stack allocated container (*std::array*), buffer size via constructor is ignored.
* default internal buffer is *std::array<char,256>*.
* added *static_assert* when trying to use *BufferAdapter* with non contiguous container.
# [4.0.1](https://github.com/fraillt/bitsery/compare/v4.0.0...v4.0.1) (2017-10-18)
### Improvements
* improved usage with Visual Studio:
* improved CMake.
* Visual Studio doesn't properly support expression SFINAE using std::void_t, so it was rewritten.
* refactorings to remove compiler warnings when using *-Wextra -Wno-missing-braces -Wpedantic -Weffc++*
* added assertion when session is empty (sessions is created via *growable* extension).
* stream adapter manually *setstate* to *std::ios_base::eofbit* when unable to read required bytes.
# [4.0.0](https://github.com/fraillt/bitsery/compare/v3.0.0...v4.0.0) (2017-10-13)
I feel that current library public API is complete, and should be stable for long time.
Most changes was made to improve performance or/and make library usage easier.
### Features
* new **flexible** syntax similar to *cereal* library.
This syntax no longer requires to specify explicit fundamental type sizes and container maxsize (container max size can be enforced by special function *maxSize*).
Be careful when using deserializing untrusted data and make sure to enforce fundamental type sizes when using on multiple platforms.
(use helper function *assertFundamentalTypeSizes* to enforce type sizes for multiple platforms)
* added streaming support, by introducing new **adapter** concept. Two adapter implementations is available: stream adapter, or buffer adapter.
* added missing std containers support: forward_list, deque, stack, queue, priority_queue, set, multiset, unordered_set, unordered_multiset.
### Breaking changes
* a lot of classes and files were renamed.
* improved error messages.
* traits reworked:
* ContainerTraits get **isContiguous**.
* TextTraits is separate from ContainerTraits, only has **length**, and **addNUL**.
* buffer traits renamed to **BufferAdapterTraits** and removed difference type.
* added **TValue** to all trait types, this is used to diagnose better errors.
* BasicBufferReader/Writer is split in two different types: **AdapterReader/Writer** and separate type that enables bit-packing operations **AdapterBitPacking(Reader/Writer)Wrapper**.
* Serializer/Deserializer reworked
* No longer copyable, because it stores adapter writer/reader.
* Removed boolByte, boolBit, and added **boolValue** and it writes bit or byte, depeding on if bit-packing is enabled or not.
* Bit-packing is enabled by calling **enableBitPacking**, if bitpacking is already enabled, this method will return same instance.
* changed defaults for *DefaultConfig*, BufferSessionsEnabled is false by default, because it doesn't work with input streams.
* serialization config no longer needs typedef *Buffer*.
# [3.0.0](https://github.com/fraillt/bitsery/compare/v2.0.1...v3.0.0) (2017-09-21)
### Features
* refactored interface, now works with C++11 compiler.
* new extension **Growable**, that allows to have forward/backward compatability within this functions serialization flow. It only allows to append new data at the end of to existing flow without breaking old consumers.
* old consumer: correctly read old interfce and ignore new data.
* new consumer: get defaults (zero values) for new fields, when reading old data.
* added new extension for associative *map* containers **ContainerMap**.
* friendly static_assert message when serializing **object**, that doesn't have **serialize** function defined.
* added **object** overload, that invokes user function/lambda with object. It is the same as calling user function directly, but makes more consistent API.
* Serializer/Deserializer now have optional *context* parameter, that might be required in some specific serialization cases.
* added traits for custom types specialization, in *details/traits.h*
* improved serialization performance: added support for fixed size buffer for BufferWriter for best performance.
* improved performance for reading/writing container sizes.
* added new method to BufferReader **getError** which returns on of three values: NO_ERROR, BUFFER_OVERFLOW, INVALID_BUFFER_DATA, also added setError method, that is only used by Deserializer.
### Breaking changes
* now all serializer/deserializer functions return void, to avoid undefined behaviour for functions parameters evaluation when using method chaining. There was no benefits apart from *nicer* syntax, but could have undefined behaviour when building complex serialization flows.
* removed **SERIALIZE** macro, and changed interface for all functions that use custom lambdas, to work with C++11. Now lambda function must capture serializer/deserializer.
* to make serializer a little bit *lighter* removed advanced features and made available as extensions via **extend** method
* removed **range** method, instead added **ValueRange** extension.
* removed **substitution** method, instead added **Entropy** extension, to sound more familiar to *entropy encoding* term.
* removed **array**, instead added fixed sizes overloads for **container**.
* removed **isValid** method from Deserializer, only BufferReader/Writer store states.
* container and text sizes representation changed, to allow much faster size reads/writes for small values.
* renamed functions:
* **ext** to **extend** and changed its interface, to make it more easy to extend.
* alias functions that write bytes directly no has *b* (meaning bytes) at the end of the name eg. *value4* now is *value4b*.
* changed BufferWriter/Reader behaviour:
* added support for fixed size buffers for better serializer performance (more than 50% improvement). Default config is resizable buffer (*std::vector<uint8_t>*).
* after serialization, call *getWrittenRange* to get valid range written to buffer, because BufferWritter for resizable buffer now always resize to *capacity* to avoid using *back_insert_iterator* for better performance.
* BufferReader has constructor with iterators (range), and raw value type pointers (begin, end).
* renamed BufferReader **isCompleted** to **isCompletedSuccessfully**, that returns true only when there is no errors and buffer is fully read.
### Bug fixes
* **readBytes** was reading in aligned mode, when scratch value from previous bit operations was zero.
* **writeBits** had incorrect assert when writing negative int64_t.
<a name="2.0.1"></a>
# [2.0.1](https://github.com/fraillt/bitsery/compare/v2.0.0...v2.0.1) (2017-08-12)
### Other notes
* added travis build status
<a name="2.0.0"></a>
# [2.0.0](https://github.com/fraillt/bitsery/compare/v1.1.1...v2.0.0) (2017-07-25)
### Features
* endianness support, default network configuration is *little endian*
* added user extensible function **ext**, to work with objects that require different serialization/deserialization path (e.g. pointers)
* **optional** extension (for *ext* function), to work with *std::optional* types
### Breaking changes
* file structure changed, added *details* folder.
* no longer support for implicit size converions for all functions (*value*, *array*, *container*), instead added helper functions with specific size, to avoid typing *s.template value<1>...* within serialization function body
* changed parameters order for all functions that use custom function (lambda)
* *BufferReader* and *BufferWriter* is now alias types for real types *BasicBufferReader/Writer&lt;DefaultConfig&gt;* (*DefaultConfig* is defined in *common.h*)
### Bug fixes
* *align* method fixed in *BufferReader*
# [1.1.1](https://github.com/fraillt/bitsery/compare/v1.0.0...v1.1.1) (2017-02-23)
### Notes
* changed folder structure
* added more BufferReader constructors
# 1.0.0 (2017-02-22)
### Features
Serialization functions:
* **value** - [fundamental types](doc/design/fundamental_types.md)
* **container** - dynamic size containers
* **array** - fixed size containers
* **text** - for c-array and std::string
* **range** - compresion for fundamental types (e.g. int between [255..512] will take up 8bits
* **substitution** - default value from list (e.g. 4d vector, that is most of the time equals to [0,0,0,1] can store only 1bit)
* **boolBit**/**boolByte** - serialize bool, as 1bit or 1byte.

View File

@@ -1,23 +1,57 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.1)
project(bitsery
LANGUAGES CXX
VERSION 5.1.0)
set(PROJECT_NAME bitsery)
set(TEST_PROJECT_NAME ${PROJECT_NAME}_tests)
project(${PROJECT_NAME})
#======== build options ===================================
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
option(BITSERY_BUILD_TESTS "Build tests" OFF)
#============= setup target ======================
add_library(bitsery INTERFACE)
# create alias, so that user could always write target_link_libraries(... Bitsery::bitsery)
# despite of bitsery target is imported or not
add_library(Bitsery::bitsery ALIAS bitsery)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -O0 -fprofile-arcs -ftest-coverage")
include(GNUInstallDirs)
target_include_directories(bitsery INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_compile_features(bitsery INTERFACE
cxx_auto_type
cxx_constexpr
cxx_lambdas
cxx_nullptr
cxx_variadic_templates)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
#=============== setup installation =======================
include(CMakePackageConfigHelpers)
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/BitseryConfigVersion.cmake
COMPATIBILITY SameMajorVersion)
install(TARGETS bitsery
EXPORT bitseryTargets
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT bitseryTargets
FILE "BitseryConfig.cmake"
NAMESPACE Bitsery::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/bitsery)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/BitseryConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/bitsery)
install(DIRECTORY include/bitsery
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
#================ handle sub-projects =====================
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/ext)
if(CMAKE_COMPILER_IS_GNUCXX)
include(CodeCoverage)
setup_target_for_coverage(${PROJECT_NAME}_coverage ${TEST_PROJECT_NAME} coverage)
if (BITSERY_BUILD_EXAMPLES)
message("build bitsery examples")
add_subdirectory(examples)
else()
message("skip bitsery examples")
endif()
add_subdirectory(examples)
#add tests
enable_testing()
add_subdirectory(tests)
if (BITSERY_BUILD_TESTS)
message("build bitsery tests")
add_subdirectory(tests)
else()
message("skip bitsery tests")
endif()

41
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,41 @@
# Contributing to Bitsery
So you want to contribute something to Bitsery? That's great!
Contributions are always very much appreciated, whether it's a bug fix, a new feature or documentation.
However, to make sure the process of accepting patches goes smoothly, you should try to follow these few simple guidelines when
you contribute:
1. Fork the repository.
2. Create new branch based on the *master* branch (`git checkout -b your_branch master`). If your contribution is a bug fix, you should name your branch `bugfix/xxx`; for a feature, it should be `feature/xxx`. Otherwise, just use your good judgment. Consistent naming of branches is appreciated since it makes the output of `git branch` easier to understand with a single glance.
3. Do your modifications on that branch. Except for special cases, your contribution should include proper unit tests and documentation.
4. Make sure your modifications did not break anything by building, running tests:
```shell
mkdir build
cd build
cmake -DBITSERY_BUILD_TESTS=ON ..
make
(cd tests; ctest)
```
or run CTest scripts and view code coverage (scripts tested on ubuntu, requires lcov for coverage):
```shell
cd scripts
ctest -S build.bitsery.cmake
./show_coverage.sh build
```
5. Commit your changes, and push to your fork (`git push origin your_branch`). Commit message should be one line short description. When applicable, please squash adjacent *wip* commits into a single *logical* commit.
6. Open a pull request against Bitsery *master* branch. Currently ongoing development is on *master*. At some point an integration branch will be set-up, and pull-requests should target that, but for now its all against master. You may see feature branches come and go, too.
If you're working with visual studio, there is how to build and run all tests from command line
```shell
mkdir build
cd build
cmake -DBITSERY_BUILD_TESTS=ON -DGTEST_ROOT="<PATH to GTEST>" -DCMAKE_CXX_FLAGS_RELEASE=/MT ..
cmake --build . --config Release
(cd tests && ctest -C Release && cd ..)
```
/MT option might be optional, depending on how gtest was built.
## Style guide
Just use your own judgment and stick to the style of the surrounding code.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 Mindaugas Vinkelis
Copyright (c) 2018 Mindaugas Vinkelis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

129
README.md
View File

@@ -1,76 +1,111 @@
# Bitsery
[![Build Status](https://travis-ci.org/fraillt/bitsery.svg?branch=master)](https://travis-ci.org/fraillt/bitsery)
[![Join the chat at https://gitter.im/bitsery/Lobby](https://badges.gitter.im/bitsery/Lobby.svg)](https://gitter.im/bitsery/Lobby)
Header only C++ binary serialization library.
It is designed around the networking requirements for multiplayer real-time fast paced games as first person shooters.
All cross-platform requirements are enforced at compile time, so serialized data do not store any run-time type information and is as small as possible.
It is designed around the networking requirements for real-time data delivery, especially for games.
## Status
All cross-platform requirements are enforced at compile time, so serialized data do not store any meta-data information and is as small as possible.
**bitsery** is in pre-release state and is looking for your feedback.
It has basic features, serialize arithmetic types, enums, containers and text, and few advanced features like value ranges and default values (substitution), but is still missing functions for delta changes and geometry compression.
> Current version do not handle Big/Little Endianness.
> Error handling on deserialization is not tested.
> **bitsery** is looking for your feedback on [gitter](https://gitter.im/bitsery/Lobby)
## Example
## Features
* Cross-platform compatible.
* Optimized for speed and space.
* No code generation required: no IDL or metadata, just use your types directly.
* 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:
* 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 (similar to [cereal](https://uscilab.github.io/cereal/)) or/and verbose syntax for better serialization control.
* Configurable endianness support.
* No macros.
## Why use bitsery
Look at the numbers and features list, and decide yourself.
| 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)*
If still not convinced read more in library [motivation](doc/design/README.md) section.
## Usage example
```cpp
#include <iostream>
#include <vector>
#include <Bitsery.h>
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
enum class MyEnum { V1,V2,V3 };
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
int i;
uint32_t i;
MyEnum e;
std::vector<float> fs;
};
//define how object should be serialized/deserialized
SERIALIZE(MyStruct) {
return s.
value(o.i).
value(o.e).
container(o.fs, 100);
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.value2b(o.e);
s.container4b(o.fs, 10);
}
using namespace bitsery;
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
MyStruct res{};
//create serializer
//1) create buffer to store data
std::vector<uint8_t> buffer;
//2) create buffer writer that is able to write bytes or bits to buffer
BufferWriter bw{buffer};
//3) create serializer
Serializer<BufferWriter> ser{bw};
Buffer buffer;
//serialize object, can also be invoked like this: serialize(ser, data)
ser.object(data);
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
//flush to buffer, before creating buffer reader
bw.flush();
//create deserializer
//1) create buffer reader
BufferReader br{buffer};
//2) create deserializer
Deserializer<BufferReader> des{br};
//deserialize same object, can also be invoked like this: serialize(des, data)
des.object(res);
//check is equal
std::cout << "is equal: " << (data.fs == res.fs && data.i == res.i && data.e == res.e) << std::endl;
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}
```
For more details go directly to [quick start](doc/tutorial/hello_world.md) tutorial.
## How to use it
This documentation comprises these parts:
* [Tutorial](doc/tutorial/README.md) - getting started.
* [Reference section](doc/README.md) - all the details.
*documentation is in progress, most parts are empty, but [contributions](CONTRIBUTING.md) are welcome.*
## Requirements
Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery.h>` and you're done.
> some **bitsery** extensions might require higher C++ standard (e.g. `StdVariant`)
## Platforms
This library was tested on
* Windows: Visual Studio 2015, MinGW (gcc 5.2)
* Linux: GCC 5.4, GCC 6.2, Clang 3.9
* Windows: Visual Studio 2015, MinGW (GCC 5.2)
* 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).

78
doc/README.md Normal file
View File

@@ -0,0 +1,78 @@
To get the most out of **Bitsery**, start with the [tutorial](tutorial/README.md).
Once you're familiar with the library consider the following reference material.
Library design:
* `fundamental types`
* `valueNb instead of value`
* `brief syntax`
* `serializer/deserializer functions overloads`
* [extending library functionality](design/extensions.md)
* `errors handling`
* `forward/backward compatibility via Growable extension`
* [pointers](design/pointers.md)
* `inheritance`
* `polymorphism`
Core Serializer/Deserializer functions (alphabetical order):
* `operator()` (4.6.1) (when brief syntax is enabled)
* `adapter` (5.0.0)
* `boolValue` (4.0.0)
* `context<T>` (4.1.0)
* `contextOrNull<T>` (4.2.0)
* `enableBitPacking` (4.0.0)
* `ext` (2.0.0)
* `object` (1.0.0)
* `text` (1.0.0)
* `value` (1.0.0)
Serializer/Deserializer extensions via `ext` method (alphabetical order):
* `BaseClass` (4.2.0)
* `CompactValue` (4.4.0)
* `CompactValueAsObject` (4.4.0)
* `Entropy` (3.0.0)
* `Growable` (3.0.0)
* `PointerOwner` (4.1.0)
* `PointerObserver` (4.1.0)
* `ReferencedByPointer` (4.1.0)
* `StdDuration` (4.6.0)
* `StdMap` (3.0.0)
* `StdOptional` (2.0.0)
* `StdQueue` (4.0.0)
* `StdSet` (4.0.0)
* `StdSmartPrt` (4.3.0)
* `StdStack` (4.0.0)
* `StdTimePoint` (4.6.0)
* `StdTuple` (4.6.0) (requires c++17)
* `StdVariant` (4.6.0) (requires c++17)
* `ValueRange` (3.0.0)
* `VirtualBaseClass` (4.2.0)
Input adapters (buffer and stream) functions:
* `align`
* `readBits`
* `readBytes`
* `readBuffer`
* `currentReadPos (get/set)` (buffer adapter only)
* `currentReadEndPos (get/set)` (buffer adapter only)
* `error (get/set)`
* `isCompletedSuccessfully`
Output adapters (buffer and stream) functions:
* `align`
* `writeBits`
* `writeBytes`
* `writeBuffer`
* `flush`
* `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:
* if you're getting static assert "please define 'serialize' function", please define **serialize** function in same namespace as object, or in **bitsery** namespace, for more info [ADL](https://en.cppreference.com/w/cpp/language/adl).
Other:
* [Contributing](../CONTRIBUTING.md)
* [Change log](../CHANGELOG.md)

71
doc/design/README.md Normal file
View File

@@ -0,0 +1,71 @@
## Motivation
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 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 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 add additional library complexity.
## A word about JSON
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.
Adding optional support for JSON doesn't come for free either.
To support JSON, additional metadata is required.
In C++ it can be achieved by two ways:
* with macros, that generate additional types like [cereal](http://uscilab.github.io/cereal/) does.
* with code generation from IDL (interface definition language) like [flatbuffers](https://google.github.io/flatbuffers/) does.
Both solutions adds additional complexity to the library. In the future C++ will get reflection system, currently static reflection was proposed to standard.
So, to avoid unnecessary library complexity it is best to forget about JSON, and stick with what machines and C++ is good at, - binary format.
# Bitsery
Bitsery is designed to be lightweight and simple to use, yet powerful and extendable library.
To ensure it works as intended it is unit tested, and has 100% code coverage.
Now let's review features in more detail.
* **Cross-platform compatible.** if same code compiles on Android, PS3 console, and your PC either x64 or x86 architecture, you are 100% sure it works.
To achieve this, bitsery specifically defines size of underlying data, hence syntax is *value\<2\>* (alias function *value2b*) instead or *value*, or *container2b* for element type of 16bits, eg int16_t.
Bitsery also applies endianness transformation if necessary.
* **Brief syntax.** If you don't like like writing code with explicitly specifying underlying type size, like *container2b* or *value8b*, you can use brief syntax.
Just include <bitsery/brief_syntax.h> and can write like in [cereal](http://uscilab.github.io/cereal/).
But do it on your own risk, and static assert using *assertFundamentalTypeSizes* function if you're planing to use it across multiple platforms.
* **Optimized for speed and space.** library itself doesn't do any allocations so data writing/reading is fast as memcpy to/from your buffer.
It also doesn't serialize any type information, all information needed is written in your code!
* **No code generation required: no IDL or metadata** since it doesn't support any other formats except binary, it doesn't need any metadata.
* **Configurable runtime error checking on deserialization** library designed to be save with untrusted network data, that's why all overloads that work on containers has *maxSize* value, unless container is static size like *std::array*.
This way bitsery ensures that no malicious data crash you. BUT, if you trust your data then you can simply disable error checks for better performance.
* **Supports forward/backward compatibility for your types** library provides access for buffer input/output adapters to directly change read/write position on the buffer.
*Growable* extension use this capability to allow forward/backward compatibility.
You can implement your own extensions to do all sorts of other things, like in-place deserialization..
* **2-in-1 declarative control flow, same code for serialization and deserialization.** only one function to define, for serialization and deserialization in same manner as *cereal* does.
It might be handy to have separate *load* and *save* functions, but Bitsery explicitly doesn't support it, to avoid any serialization deserialization divergence, because it is very hard to catch an errors if you make a bug in one of these functions.
The only way around this through extensions, write your custom flow once, and reuse where you need them.
* **Allows fine-grained serialization control** this is a feature that no other libraries provides.
Bitsery allows to use bit-level operations and has two extensions that use them:
* *ValueRange*,- if you have a *int16_t* data type, but you know that your object only stores values in \[0..1000\] range, it will write 10bits, instead of 16bits. ValueRanges also works with floats.
* *Entropy*,- full term is *entropy encoding*, which means that when you have most common value, or multiple values, it will write just few bits instead of full object.
Eg.: imagine that you have a struct Person{ int32_t Id; string Profession; }.
You might know that mostly there are young persons, so the most common value will be equal to: "Student", "Child", "NoProfession", in this case you'll pay 2bits for each record, but write no data if string matches.
Using these bit-level operations and extensions you can compose your own extensions for vectors, matrices or any other types.
Further more, all other operations will not align data automatically for you, so data will be compressed as much as possible.
* One more advanced and dangerous feature, is ability to have serialization context, so you can control your serialization flow at runtime, but make sure that these contexts are in sync between serializer and deserializer.
One possible use case for serialization context is to pass min/max ranges for *ValueRange* when your information changes at runtime.
* **Easily extendable** library is designed to be easily extendable for any type and flow.
You want to support your custom container, its fine there is *ContainerTraits* for this, only few methods required to implement.
To use same container for buffer writing/reading add specialization to *BufferAdapterTraits*.
You want to customize serialization flow - use extensions, only two methods to define, and *ExtensionTraits* to further customize usage.
* **Configurable endianness support.** default is *Little Endian*, but if your primary target is PowerPC architecture, eg. PlayStation3, just change your configuration to be *Big Endian*.
* **No macros.** Not so much to say, if you are like me, then it's a feature :)
It should also be noted, that extensions that allocate memory (e.g. pointer serialization/deserialization) allow to provide memory resource (similar to C++17 `std::pmr::memory_resource`).

2
doc/design/errors.md Normal file
View File

@@ -0,0 +1,2 @@
errors handling design
usage of NotDefinedType, and how it helps to reduce error stack

51
doc/design/extensions.md Normal file
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.

1
doc/design/function_n.md Normal file
View File

@@ -0,0 +1 @@
valueN instead of value

View File

View File

@@ -0,0 +1 @@
ints chars floats (except bool)

39
doc/design/pointers.md Normal file
View File

@@ -0,0 +1,39 @@
*document in progress*
## Extensions
Raw pointers are managed by three extensions:
* **PointerOwner** - manages a lifetime of the pointer, creates or destroys if required.
* **PointerObserver** - doesn't own pointer so it doesn't create or destroy anything.
* **ReferencedByPointer** - when a non-owning pointer (*PointerObserver*) points to reference type, this extension marks this object as a valid target for PointerObserver.
"Smart" pointers, from c++ standard lib (std), are managed by:
* **StdSmartPtr** - can accept unique_ptr, shared_ptr and weak_ptr
## Implementation details
All aforementioned extensions derive from single base class `PointerObjectExtensionBase`.
This base class accepts three template parameters that customise its behaviour.
* `TPtrManager\<T\>` - describes how particular pointer type should be handled:
pointer creation/destruction describes a type of pointer (e.g. owning, shared, observer), and how to actually get value for pointer object.
This is the place to start if you want to implement pointer support for your custom type. \<T\> is type of pointer object, e.g. `std::unique_ptr<MyType>`, `MyType*`
* `TPolymorphicContext\<RTTI\>` - provides the functionality to register class hierarchies for your types with serializer, and deserializer, in order to polymorphically serialize/deserialize objects.
\<RTTI\> template parameter provides runtime information about a type that is used to construct class hierarchies and save them to read/write them to buffer.
* `RTTI` - this template parameter provides information if a type is polymorphic, and if it is, then it is used in `TPolymorphicContext\<RTTI\>`.
Some pointer managers, like `PointerObserver` and `ReferencedByPointer` never requires polymorphic context. In these cases, you need to provide RTTI that will return `isPolymorphic`=false for all types.
By default all pointers extensions use `StandardRTTI` from `/ext/utils/rtti_utils.h` that internally uses `typeid` and `dynamic_cast`.
If your environment doesn't allow RTTI, you can provide your own RTTI for your types.
## Allocation and memory resources
Allocation is implemented using memory resources (similar to `std::pmr::memory_resource` from c++17),
and it is called `MemResourceBase` from "ext/utils/memory_allocator.h". The core difference between standard one
is that it has additional `size_t typeId` field for `allocate` and `deallocate` methods, and this typeId is returned from `RTTI`.
There are few options to customise pointer allocation for your pointers by using `MemResourceBase`:
* invoke `setMemResource` in `PointerLinkingContext`.
* pass memory resource to pointer manager constructor, along with boolean parameter that specifies if this memory resource should propagate when deserializing child objects.
If no memory resource is provided, then `MemResourceNewDelete` is used, which calls `::operator new(bytes)` and `::operator delete(ptr)`.
**IMPORTANT**: there are few things that you should know to correctly use custom allocations with `StdSmartPtr`:
* Memory resource must live as long as the last object, that was allocated with it (this is required by std::shared_ptr, custom deleter is provided, that will be able to deallocate correctly when a shared pointer is destroyed).
* std::unique_ptr is allocated and deallocated using provided memory resource.
If you create unique pointers your self, make sure that it uses the same memory resource, because bitsery will not call `.reset` method on pointer, and instead `.release()` it and deallocate manually.

View File

@@ -0,0 +1,6 @@
*document in progress*
* NoError,
* BUFFER_OVERFLOW,
* INVALID_BUFFER_DATA
* write what happens when data is corrupted
* how growable effect error codes when deserializing old data format.

View File

@@ -0,0 +1,7 @@
### BufferReader.isCompletedSuccessfully()
Returns true when buffer was fully read, and there was no [errors](buf_get_error.md).
If buffer contains multiple serialized objects and you want to know deserialization state for each object, then use [getError](buf_get_error.md) after reading each object.
Use this function when buffer contains one object, or you know that you read all data.

8
doc/tutorial/README.md Normal file
View File

@@ -0,0 +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:
* [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

@@ -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);
}
});
}
```

124
doc/tutorial/hello_world.md Normal file
View File

@@ -0,0 +1,124 @@
# Quick Start
This is a quick guide to get **bitsery** up and running in a matter of minutes.
The only prerequisite for running bitsery is a modern C++11 compliant compiler, such as GCC 4.9.4, clang 3.4, MSVC 2015, or newer.
Older versions might work, but they have not been tested.
## Get bitsery
**bitsery** can be directly included in your project or installed anywhere you can access header files.
Grab the latest version, and include directory `bitsery_base_dir/include/` to your project.
There's nothing to build or make - **bitsery** is header only.
## Include required headers and define some helper types
```cpp
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/string.h>
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
```
**bitsery** is very lightweight, so we need to explicitly include what we need.
Include | Description
--|--
`<bitsery/bitsery.h>` | This is a core header, that includes our Serializer and Deserializer.
`<bitsery/adapter/buffer.h>` | In order to write/read data, we need a specific adapter, depending on what underlying buffer will be. In this example, we'll be using `std::vector` as our buffer, so we include the buffer adapter.
`<bitsery/traits/...>` | Traits tell the library how to efficiently serialize a particular container. Many common STL containers are supported out of the box.
Create alias types for *InputAdapter* and *OutputAdapter* using our vector as buffer.
## Add serialization method for your type
**bitsery** needs to know which data members to serialize in your classes.
Let it know by implementing a serialize method for your type:
```cpp
struct MyStruct {
uint32_t i;
char str[6];
std::vector<float> fs;
};
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.text1b(o.str);
s.container4b(o.fs, 100);
};
```
**bitsery** also allows to define serialize function in side your class, and can also serialize private class members, just make *friend bitsery::Access;*
**bitsery** supports two ways how to describe your serialization flow: *verbose syntax* (as in example) or *brief syntax*, similar to *cereal* library, just include `<bitsery/brief_syntax.h>` to use it.
This example we choosed probably unfamiliar verbose syntax, so lets explain core functionality that you'll use all the time:
* **s.value4b(o.i);** serialize fundamental types (ints, floats, enums) value**4b** means, that data type is 4 bytes. If you use same code on different machines, if it compiles it means it is compatible.
* **s.text1b(o.str);** serialize text (null-terminated) of char type, if you use *wchar* then you would write *text2b* 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.
External serialization functions should be placed either in the same namespace as the types they serialize or in the **bitsery** namespace so that the compiler can find them properly.
## Serialization and deserialization
Create buffer and use helper functions for serialization and deserialization.
```cpp
Buffer buffer;
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
```
These helper functions use default configuration *bitsery::DefaultConfig*
* **quickSerialization** create serializer using output adapter, serializes data and returns written size.
* **quickDeserialization** create deserializer using input adapter, deserializes to object, and returns deserialization state.
deserialization state has two properties, error code and bool that indicates if buffer was fully read and there is no errors.
## Full example code
```cpp
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
#include <bitsery/traits/string.h>
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
struct MyStruct {
uint32_t i;
char str[6];
std::vector<float> fs;
};
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.text1b(o.str);
s.container4b(o.fs, 100);
}
int main() {
MyStruct data{8941, "hello", {15.0f, -8.5f, 0.045f}};
MyStruct res{};
Buffer buffer;
auto writtenSize = bitsery::quickSerialization(OutputAdapter{buffer}, data);
auto state = bitsery::quickDeserialization(InputAdapter{buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && std::strcmp(data.str, res.str) == 0);
}
```
**currently documentation and tutorial is progress, but for more usage examples see examples folder**

View File

@@ -20,19 +20,21 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
cmake_minimum_required(VERSION 3.1)
project(bitsery_examples CXX)
cmake_minimum_required(VERSION 3.2)
if (NOT TARGET Bitsery::bitsery)
message(FATAL_ERROR "Bitsery::bitsery alias not set. Please generate CMake from bitsery root directory.")
endif()
include_directories(${CMAKE_SOURCE_DIR}/include)
file(GLOB ExampleFiles ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB EXAMPLE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
FOREACH(EXAMPLE ${EXAMPLE_FILES})
get_filename_component(EXAMPLE_NAME ${EXAMPLE} NAME_WE)
add_executable(${EXAMPLE_NAME} ${EXAMPLE})
set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD 14)
set_property(TARGET ${EXAMPLE_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
ENDFOREACH()
foreach(ExampleFile ${ExampleFiles})
get_filename_component(ExampleName ${ExampleFile} NAME_WE)
add_executable(bitsery.example.${ExampleName} ${ExampleFile})
target_link_libraries(bitsery.example.${ExampleName} PRIVATE Bitsery::bitsery)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(bitsery.example.${ExampleName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
endif()
endforeach()

View File

@@ -1,75 +1,48 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
//include bitsery.h to get serialization and deserialization classes
#include <bitsery/bitsery.h>
//in ordered to serialize/deserialize data to buffer, include buffer adapter
#include <bitsery/adapter/buffer.h>
//bitsery itself doesn't is lightweight, and doesnt include any unnessessary files,
//traits helps library to know how to use types correctly,
//in this case we'll be using vector both, to serialize/deserialize data and to store use as a buffer.
#include <bitsery/traits/vector.h>
#include <iostream>
#include <vector>
#include <Bitsery.h>
enum class MyEnum { V1,V2,V3 };
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
int i;
uint32_t i;
MyEnum e;
std::vector<float> fs;
};
//define how object should be serialized/deserialized
SERIALIZE(MyStruct) {
return s.
value(o.i).
value(o.e).
container(o.fs, 100);
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);//fundamental types (ints, floats, enums) of size 4b
s.value2b(o.e);
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 = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
MyStruct res{};
//create serializer
//1) create buffer to store data
std::vector<uint8_t> buffer;
//2) create buffer writer that is able to write bytes or bits to buffer
BufferWriter bw{buffer};
//3) create serializer
Serializer<BufferWriter> ser{bw};
//create buffer to store data
Buffer buffer;
//use quick serialization function,
//it will use default configuration to setup all the nesessary steps
//and serialize data to container
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
//serialize object, can also be invoked like this: serialize(ser, data)
ser.object(data);
//same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
//flush to buffer, before creating buffer reader
bw.flush();
//create deserializer
//1) create buffer reader
BufferReader br{buffer};
//2) create deserializer
Deserializer<BufferReader> des{br};
//deserialize same object, can also be invoked like this: serialize(des, data)
des.object(res);
//check is equal
std::cout << "is equal: " << (data.fs == res.fs && data.i == res.i && data.e == res.e) << std::endl;
}
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}

62
examples/bit_packing.cpp Normal file
View File

@@ -0,0 +1,62 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
//we'll be using std::array as a buffer type, so include traits for this
#include <bitsery/traits/array.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
//include extension that will allow to compress our data
#include <bitsery/ext/value_range.h>
namespace MyTypes {
struct Vec3 { float x, y, z; };
struct Monster {
Vec3 pos;
std::vector<Vec3> path;
std::string name;
};
template<typename S>
void serialize(S& s, MyTypes::Vec3 &o) {
s.value4b(o.x);
s.value4b(o.y);
s.value4b(o.z);
}
template <typename S>
void serialize (S& s, Monster& o) {
s.text1b(o.name, 20);
s.object(o.pos);
//compress path in a range of -1.0 .. 1.0 with 0.01 precision
//enableBitPacking creates separate serializer/deserializer object, that contains bit packing operations
s.enableBitPacking([&o](typename S::BPEnabledType& sbp) {
sbp.container(o.path, 1000, [](typename S::BPEnabledType& sbp, Vec3& vec3) {
constexpr bitsery::ext::ValueRange<float> range{-1.0f,1.0f, 0.01f};
sbp.ext(vec3.x, range);
sbp.ext(vec3.y, range);
sbp.ext(vec3.z, range);
});
});
}
}
//use fixed-size buffer
using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyTypes::Monster data{};
data.name = "lew";
//create buffer to store data to
Buffer buffer{};
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

44
examples/brief_syntax.cpp Normal file
View File

@@ -0,0 +1,44 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
//to use brief syntax always include this header
#include <bitsery/brief_syntax.h>
//we also need additional traits to work with container types,
//instead of including <bitsery/traits/vector.h> for vector traits, now we also need traits to work with brief_syntax types.
//so include everything from <bitsery/brief_syntax/...> instead of <bitsery/traits/...>
//otherwise we'll get static assert error, saying to define serialize function.
#include <bitsery/brief_syntax/vector.h>
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
uint32_t i;
MyEnum e;
std::vector<float> fs;
//define serialize function as usual
template <typename S>
void serialize(S& s) {
//now we can use brief syntax with
s(i, e, fs);
}
};
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyStruct data{8941, MyEnum::V2, {15.0f, -8.5f, 0.045f}};
MyStruct res{};
//serialization, deserialization flow is unchanged as in basic usage
Buffer buffer;
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.fs == res.fs && data.i == res.i && data.e == res.e);
}

View File

@@ -0,0 +1,112 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
// include extensions to work with tuples and variants
// these extesions only work with C++17
#if __cplusplus > 201402L
#include <bitsery/ext/std_tuple.h>
#include <bitsery/ext/std_variant.h>
// let's include this extension to make it more interesting :)
#include <bitsery/ext/compact_value.h>
struct MyStruct {
std::vector<int32_t> v{};
float f{};
bool operator==(const MyStruct& rhs) const {
return v == rhs.v && f == rhs.f;
}
};
template<typename S>
void serialize(S& s, MyStruct& o) {
s.container4b(o.v, 1000);
s.value4b(o.f);
}
// this will be the type that we want to serialize/deserialize
using MyTuple = std::tuple<float, MyStruct>;
using MyVariant = std::variant<int64_t, MyTuple, MyStruct>;
// 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, 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, 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);}
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
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)
// ext::OverloadExtObject<MyTuple, ext::StdTuple<ext::OverloadValue<float, 4>>>{},
// we can also override default 'serialize' function by creating an overloading for that type
[](S& s, MyStruct& o) {
s.value4b(o.f);
s.container(o.v, 1000, [](S& s, int32_t& v) {
s.ext4b(v, bitsery::ext::CompactValue{});
});
},
// NOTE.
// it is possible to provide "auto" as type parameter
// this will allow you to override all default 'serialize' functions
// but in this case it will not be called, because we have explicitly provided overloads for all variant types
// also note, that first parameter (serializer) is also "auto", this is required, so that it would be least specialized case
// otherwise it will not compile if you any ext::Overload* helper defined, because it will have ambiguous definitions
// (ext::OverLoad* defines (templated_type& s, concrete_type& o) and lambda would be (concrete_type& s, templated_type& o))
[](auto& , auto& ) {
assert(false);
}
});
}
//some helper types
using Buffer = std::vector<uint8_t>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyVariant data{MyTuple{-7549, {{-451, 2, 968, 75, 4, 156, 49}, 874.4f}}};
// MyVariant data{MyStruct{{-451, 2, 968, 75, 4, 156, 49}, 874.4f}};
MyVariant res{};
//create buffer to store data
Buffer buffer;
//use quick serialization function,
//it will use default configuration to setup all the nesessary steps
//and serialize data to container
auto writtenSize = 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 = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data == res);
}
#else
#if defined(_MSC_VER)
#pragma message("example works only on c++17")
#else
#warning "example works only on c++17"
#endif
int main() {
return 0;
}
#endif

View File

@@ -0,0 +1,98 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/string.h>
#include <bitsery/traits/vector.h>
#include <bitsery/ext/value_range.h>
namespace MyTypes {
struct Monster {
Monster() = default;
Monster(std::string _name, uint32_t minDmg, uint32_t maxDmg)
:name{_name}, minDamage{minDmg}, maxDamage{maxDmg} {}
std::string name{};
uint32_t minDamage{};
uint32_t maxDamage{};
//...
};
struct GameState {
std::vector<Monster> monsters;
};
//default flow for monster
template <typename S>
void serialize (S& s, Monster& o) {
s.text1b(o.name, 20);
s.value4b(o.minDamage);
s.value4b(o.maxDamage);
}
template<typename S>
void serialize(S& s, GameState &o) {
//we can have multiple types in context with std::tuple
//if data type doesn't match then it will be compile time error
//NOTE: if context is optional then you can call contextOrNull<T>, and it will return null if T doesn't exists
auto maxMonsters = s.template context<int>();
auto& dmgRange = s.template context<std::pair<uint32_t, uint32_t>>();
s.container(o.monsters, maxMonsters, [&dmgRange] (S& s, Monster& m) {
s.text1b(m.name, 20);
//we know min/max damage range for monsters, so we can use this range instead of full value
bitsery::ext::ValueRange<uint32_t> range{dmgRange.first, dmgRange.second};
//enable bit packing
s.enableBitPacking([&m, &range](typename S::BPEnabledType& sbp) {
sbp.ext(m.minDamage, range);
sbp.ext(m.maxDamage, range);
});
});
}
}
//context can contain multiple types by wrapping these types in std::tuple
//in serialization function we can get type that we need like this:
// s.template context<int>();
//this templated version also works if our context is the same as cast:
// struct MyContext {...};
// ...
// s.template context<MyContext>();
//NOTE:
// if your context has no additional usage outside of serialization flow,
// then you can create it internally via configuration (see inheritance.cpp)
using Context = std::tuple<int, std::pair<uint32_t, uint32_t>>;
//use fixed-size buffer
using Buffer = std::vector<uint8_t>;
// define adapter types,
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
MyTypes::GameState data{};
data.monsters.push_back({"weaksy", 100, 200});
data.monsters.push_back({"bigsy", 500, 1000});
data.monsters.push_back({"tootoo", 350, 750});
//set context
Context ctx{};
//max monsters
std::get<0>(ctx) = 4;
//damage range
std::get<1>(ctx).first = 100;
std::get<1>(ctx).second = 1000;
//create buffer to store data to
Buffer buffer{};
auto writtenSize = bitsery::quickSerialization(ctx, OutputAdapter{buffer}, data);
MyTypes::GameState res{};
auto state = bitsery::quickDeserialization(ctx, InputAdapter{buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

55
examples/file_stream.cpp Normal file
View File

@@ -0,0 +1,55 @@
#include <bitsery/bitsery.h>
//in order to work with streams include stream adapter
#include <bitsery/adapter/stream.h>
#include <fstream>
#include <iostream>
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
uint32_t i;
MyEnum e;
double f;
};
//define how object should be serialized/deserialized
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.value2b(o.e);
s.value8b(o.f);
}
int main() {
//set some random data
MyStruct data{8941, MyEnum::V2, 0.045};
MyStruct res{};
//open file stream for writing and reading
auto fileName = "test_file.bin";
std::fstream s{fileName, s.binary | s.trunc | s.out};
if (!s.is_open()) {
std::cout << "cannot open " << fileName << " for writing\n";
return 0;
}
//we cannot use quick serialization function, because streams cannot use writtenBytesCount method
bitsery::Serializer<bitsery::OutputBufferedStreamAdapter> ser{s};
ser.object(data);
//flush to writer
ser.adapter().flush();
s.close();
//reopen for reading
s.open(fileName, s.binary | s.in);
if (!s.is_open()) {
std::cout << "cannot open " << fileName << " for reading\n";
return 0;
}
//same as serialization, but returns deserialization state as a pair
//first = error code, second = is buffer was successfully read from begin to the end.
auto state = bitsery::quickDeserialization<bitsery::InputStreamAdapter>(s, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
assert(data.f == res.f && data.i == res.i && data.e == res.e);
}

View File

@@ -0,0 +1,92 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
//include traits for types, that we'll be using
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/vector.h>
//include extension that will allow to have backward/forward compatibility
#include <bitsery/ext/growable.h>
namespace MyTypes {
//define data
enum Color:uint8_t { Red, Green, Blue };
struct Vec3 { float x, y, z; };
struct Weapon {
std::string name{};
int16_t damage{};
Weapon() = default;
Weapon(const std::string& _name, int16_t dmg):name{_name}, damage{dmg} {}
private:
//define serialize function as private, and give access to bitsery
friend bitsery::Access;
template <typename S>
void serialize (S& s) {
//forward/backward compatibility for monsters
s.ext(*this, bitsery::ext::Growable{}, [](S& s, Weapon& o1) {
s.text1b(o1.name, 20);
s.value2b(o1.damage);
});
}
};
struct Monster {
Vec3 pos;
int16_t mana;
int16_t hp;
std::string name;
std::vector<uint8_t> inventory;
Color color;
std::vector<Weapon> weapons;
Weapon equipped;
std::vector<Vec3> path;
};
template <typename S>
void serialize(S& s, Vec3& o) {
s.value4b(o.x);
s.value4b(o.y);
s.value4b(o.z);
}
template <typename S>
void serialize (S& s, Monster& o) {
//forward/backward compatibility for monsters
s.ext(o, bitsery::ext::Growable{}, [](S& s, Monster& o1) {
s.value1b(o1.color);
s.value2b(o1.mana);
s.value2b(o1.hp);
s.object(o1.equipped);
s.object(o1.pos);
s.container(o1.path, 1000);
s.container(o1.weapons, 100);
s.container1b(o1.inventory, 50);
s.text1b(o1.name, 20);
});
}
}
//use fixed-size buffer
using Buffer = std::array<uint8_t, 10000>;
using OutputAdapter = bitsery::OutputBufferAdapter<Buffer>;
using InputAdapter = bitsery::InputBufferAdapter<Buffer>;
int main() {
//set some random data
MyTypes::Monster data{};
data.name = "lew";
data.weapons.push_back(MyTypes::Weapon{"GoodWeapon", 100});
//create buffer to store data to
Buffer buffer{};
//since we're using different configuration, we cannot use quickSerialization function.
auto writtenSize = bitsery::quickSerialization<OutputAdapter>(buffer, data);
MyTypes::Monster res{};
//deserialize
auto state = bitsery::quickDeserialization<InputAdapter>({buffer.begin(), writtenSize}, res);
assert(state.first == bitsery::ReaderError::NoError && state.second);
}

104
examples/inheritance.cpp Normal file
View File

@@ -0,0 +1,104 @@
//
//this example covers all the corner cases that can happen using inheritance
//in reality virtual inherintance is usually avoided, so your code would look much simpler.
//
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
//include inheritance extension
//this header contains two extensions, that specifies inheritance type of base class
// BaseClass - normal inheritance
// VirtualBaseClass - when virtual inheritance is used
//in order for virtual inheritance to work, InheritanceContext is required. for normal inheritance it is not required
#include <bitsery/ext/inheritance.h>
using bitsery::ext::BaseClass;
using bitsery::ext::VirtualBaseClass;
struct Base {
uint8_t x{};
//Base doesn't have to be polymorphic class, inheritance works at compile-time.
};
template <typename S>
void serialize(S& s, Base& o) {
s.value1b(o.x);
}
struct Derive1:virtual Base {// virtually inherits from base
uint8_t y1{};
};
template <typename S>
void serialize(S& s, Derive1& o) {
//define virtual inheritance, it will not compile if InheritanceContext is not defined in serializer/deserializer
s.ext(o, VirtualBaseClass<Base>{});
s.value1b(o.y1);
}
//to make it more interesting, serialize private member
struct Derived2:virtual Base {
explicit Derived2(uint8_t y):y2{y} {}
uint8_t getY2() const {
return y2;
};
private:
friend bitsery::Access;
uint8_t y2{};
template <typename S>
void serialize(S& s) {
//notice virtual inheritance
s.ext(*this, VirtualBaseClass<Base>{});
s.value1b(y2);
}
};
struct MultipleInheritance: Derive1, Derived2 {
explicit MultipleInheritance(uint8_t y2):Derived2{y2} {}
uint8_t z{};
};
template <typename S>
void serialize(S& s, MultipleInheritance& o) {
//has two bases, serialize them separately
s.ext(o, BaseClass<Derive1>{});
s.ext(o, BaseClass<Derived2>{});
s.value1b(o.z);
}
namespace bitsery {
// call to serialize function with Derived2 and MultipleInheritance is ambiguous,
// it matches two serialize functions: Base classes non-member fnc and Derived2 member fnc
// we need explicitly select which function to use
template <>
struct SelectSerializeFnc<Derived2>:UseMemberFnc {};
//multiple inheritance has non-member serialize function defined
template <>
struct SelectSerializeFnc<MultipleInheritance>:UseNonMemberFnc {};
}
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
MultipleInheritance data{98};
data.x = 254;
data.y1 = 47;
data.z = 1;
Buffer buf{};
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};
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

@@ -0,0 +1,62 @@
//
// example of how to deserialize non default constructible objects
//
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
class MyData {
//define your private data
float _x{0};
float _y{0};
//make bitsery:Access friend
friend class bitsery::Access;
//create default constructor, don't worry about class invariant, it will be restored in deserialization
MyData() = default;
//define serialize function
template <typename S>
void serialize(S& s) {
s.value4b(_x);
s.value4b(_y);
}
public:
//define non default public constructor
MyData(float x, float y):_x{x}, _y{y} {}
//this is for convenience
bool operator ==(const MyData&rhs) const {
return _x == rhs._x && _y == rhs._y;
}
};
//some helper types
using Buffer = std::vector<uint8_t>;
using Writer = bitsery::OutputBufferAdapter<Buffer>;
using Reader = bitsery::InputBufferAdapter<Buffer>;
int main() {
//initialize our data
std::vector<MyData> data{};
data.emplace_back(145.4f, 84.48f);
std::vector<MyData> res{};
//create buffer
Buffer buffer{};
//we cant use quick (de)serialization helper methods, because we ant to serialize container directly
//create writer and serialize container
bitsery::Serializer<Writer> ser{buffer};
ser.container(data, 10);
ser.adapter().flush();
//create reader and deserialize container
bitsery::Deserializer<Reader> des{buffer.begin(), ser.adapter().writtenBytesCount()};
des.container(res, 10);
//check if everything went ok
assert(des.adapter().error() == bitsery::ReaderError::NoError && des.adapter().isCompletedSuccessfully());
assert(res == data);
}

145
examples/raw_pointers.cpp Normal file
View File

@@ -0,0 +1,145 @@
#include <bitsery/bitsery.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/traits/vector.h>
//include pointers extension
//this header contains multiple extensions for different pointer types and pointer linking context,
//that validates pointer ownership and checks if there are and no dangling pointers after serialization/deserialization.
//dangling pointer in this context means, that non-owning pointer points to data, that was not serialized.
#include <bitsery/ext/pointer.h>
using bitsery::ext::ReferencedByPointer;
using bitsery::ext::PointerObserver;
using bitsery::ext::PointerOwner;
using bitsery::ext::PointerType ;
enum class MyEnum:uint16_t { V1,V2,V3 };
struct MyStruct {
MyStruct(uint32_t i_, MyEnum e_, std::vector<float> fs_)
:i{i_},
e{e_},
fs{fs_} {}
MyStruct():MyStruct{0, MyEnum::V1, {}} {}
uint32_t i;
MyEnum e;
std::vector<float> fs;
};
template <typename S>
void serialize(S& s, MyStruct& o) {
s.value4b(o.i);
s.value2b(o.e);
s.container4b(o.fs, 10);
}
//our test data
struct Test1Data {
//regular data, nothing fancy here
MyStruct o1;
int32_t i1;
//these container elements can be referenced by pointers
std::vector<MyStruct> vdata;
//container that holds non owning pointers (observers),
std::vector<MyStruct*> vptr;
//treat it as is observer
MyStruct* po1;
//we treat this as owner (responsible for allocation/deallocation
int32_t* pi1;
private:
friend bitsery::Access;
template <typename S>
void serialize(S& s) {
//just a regular fields
s.object(o1);
s.value4b(i1);
//set container elements to be candidates for non-owning pointers
s.container(vdata, 100, [](S& s, MyStruct& d){
s.ext(d, ReferencedByPointer{});
});
//contains non owning pointers
//
//IMPORTANT !!!
//ALWAYS ACCEPT BY REFERENCE like this: T* (&obj)
//if using c++14, then auto& always works.
//
//you can also serialize non owning pointers first, pointer linking context will keep track on them
//and as soon as pointer owner data is deserialized, all non-owning pointers will be updated
s.container(vptr, 100, [](S& s, MyStruct* (&d)){
s.ext(d, PointerObserver{});
});
//observer
s.ext(po1, PointerObserver{});
//owner, mark it as not null
s.ext4b(pi1, PointerOwner{PointerType::NotNull});
}
};
//some helper types
using Buffer = std::vector<uint8_t>;
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:
// std::tuple<MyContext,ext::PointerLinkingContext>
//and other code will work as expected as long as it cast to proper type.
//see context_usage.cpp for usage example
int main() {
//set some random data
Test1Data data{};
data.vdata.emplace_back(8941, MyEnum::V1, std::vector<float>{4.4f});
data.vdata.emplace_back(15478, MyEnum::V2, std::vector<float>{15.0f});
data.vdata.emplace_back(59, MyEnum::V3, std::vector<float>{-8.5f, 0.045f});
//container of non owning pointers (observers)
data.vptr.emplace_back(nullptr);
data.vptr.emplace_back(std::addressof(data.vdata[0]));
data.vptr.emplace_back(std::addressof(data.vdata[2]));
//regular fields
data.o1 = MyStruct{4, MyEnum::V2, {57.078f}};
data.i1 = 9455;
//observer
data.po1 = std::addressof(data.vdata[1]);
//owning pointer
data.pi1 = new int32_t{};
//create buffer to store data
Buffer buffer{};
size_t writtenSize{};
//in order to use pointers, we need to pass pointer linking context serializer/deserializer
{
bitsery::ext::PointerLinkingContext ctx{};
writtenSize = quickSerialization(ctx, Writer{buffer}, data);
//make sure that pointer linking context is valid
//this ensures that all non-owning pointers points to data that has been serialized,
//so we can successfully reconstruct pointers after deserialization
assert(ctx.isValid());
}
Test1Data res{};
{
bitsery::ext::PointerLinkingContext ctx{};
auto state = quickDeserialization(ctx, Reader{buffer.begin(), writtenSize}, res);
//check if everything went find
assert(state.first == bitsery::ReaderError::NoError && state.second);
//also check for dangling pointers, after deserialization
assert(ctx.isValid());
}
//owning pointers owns data
assert(*res.pi1 == *data.pi1);
assert(res.pi1 != data.pi1);
//observers, points to other data
assert(res.vptr[0] == nullptr);
assert(res.vptr[1] == std::addressof(res.vdata[0]));
assert(res.vptr[2] == std::addressof(res.vdata[2]));
assert(res.po1 == std::addressof(res.vdata[1]));
//delete raw owning pointers
delete data.pi1;
delete res.pi1;
}

View File

@@ -0,0 +1,269 @@
//
// Created by fraillt on 18.4.26.
//
#include <cassert>
#include <memory>
#include <bitsery/bitsery.h>
#include <bitsery/traits/vector.h>
#include <bitsery/adapter/buffer.h>
#include <bitsery/ext/pointer.h>
#include <bitsery/ext/inheritance.h>
#include <bitsery/ext/std_smart_ptr.h>
//in order to work with polymorphic types, we need to describe few steps:
// 1) describe relationships between base and derived types
// this will allow to know what are possible types reachable from base class
// 2) bind serializer to base class
// this will allow to iterate through all types, and add serialization functions,
// without this step compiler would simply remove functions that are not bound at compile-time even it we use type at runtime.
using bitsery::ext::BaseClass;
using bitsery::ext::PointerObserver;
using bitsery::ext::StdSmartPtr;
//define our data structures
struct Color {
float r{}, g{}, b{};
bool operator == (const Color& o) const {
return std::tie(r, g, b) ==
std::tie(o.r, o.g, o.b);
}
};
struct Shape {
Color clr{};
virtual ~Shape() = 0;
};
Shape::~Shape() = default;
struct Circle : Shape {
int32_t radius{};
bool operator == (const Circle& o) const {
return std::tie(radius, clr) ==
std::tie(o.radius, o.clr);
}
};
struct Rectangle : Shape {
int32_t width{};
int32_t height{};
bool operator == (const Rectangle& o) const {
return std::tie(width, height, clr) ==
std::tie(o.width, o.height, o.clr);
}
};
struct RoundedRectangle : Rectangle {
int32_t radius{};
bool operator == (const RoundedRectangle& o) const {
return std::tie(radius, static_cast<const Rectangle&>(*this)) ==
std::tie(o.radius, static_cast<const Rectangle&>(o));
}
};
//define serialization functions
template<typename S>
void serialize(S &s, Color &o) {
//in real world scenario, it might be possible to serialize this using ValueRange, to map values in smaller space
//but for the sake of this example keep it simple
s.value4b(o.r);
s.value4b(o.g);
s.value4b(o.b);
}
template<typename S>
void serialize(S &s, Shape &o) {
s.object(o.clr);
}
template<typename S>
void serialize(S &s, Circle &o) {
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.radius);
}
template<typename S>
void serialize(S &s, Rectangle &o) {
s.ext(o, bitsery::ext::BaseClass<Shape>{});
s.value4b(o.width);
s.value4b(o.height);
}
template<typename S>
void serialize(S &s, RoundedRectangle &o) {
s.ext(o, bitsery::ext::BaseClass<Rectangle>{});
s.value4b(o.radius);
}
//define our test structure
struct SomeShapes {
std::vector<std::shared_ptr<Shape>> sharedList;
std::unique_ptr<Shape> uniquePtr;
//weak ptr and refPtr will point to sharedList
std::weak_ptr<Shape> weakPtr;
Shape* refPtr;
};
//creates object, and populates some data
SomeShapes createData() {
SomeShapes data{};
{
auto tmp = new RoundedRectangle{};
tmp->height = 151572;
tmp->width = 488795;
tmp->radius = 898;
tmp->clr.r = 0.5f;
tmp->clr.g = 1.0f;
tmp->clr.b = 1.0f;
data.uniquePtr.reset(tmp);
}
{
auto tmp = new Circle{};
tmp->radius = 75987;
tmp->clr.r = 0.5f;
tmp->clr.g = 0.0f;
tmp->clr.b = 1.0f;
data.sharedList.emplace_back(tmp);
}
{
auto tmp = new Rectangle{};
tmp->height = 15157;
tmp->width = 48879;
tmp->clr.r = 1.0f;
tmp->clr.g = 0.0f;
tmp->clr.b = 0.0f;
data.sharedList.emplace_back(tmp);
}
data.weakPtr = data.sharedList[0];
data.refPtr = data.sharedList[1].get();
return data;
}
template<typename S>
void serialize(S &s, SomeShapes &o) {
s.ext(o.uniquePtr, StdSmartPtr{});
// to make things more interesting first serialize weakPtr and refPtr,
// even though objects that weakPtr and refPtr is serialized later,
// bitsery will work regardless
s.ext(o.weakPtr, StdSmartPtr{});
s.ext(o.refPtr, PointerObserver{});
s.container(o.sharedList, 100, [](S& s, std::shared_ptr<Shape> &item) {
s.ext(item, StdSmartPtr{});
});
}
// STEP 1
// define relationships between base and derived classes
namespace bitsery {
namespace ext {
//for each base class define DIRECTLY derived classes
//e.g. PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle, RoundedRectangle>
// is incorrect, because RoundedRectangle does not directly derive from Shape
template<>
struct PolymorphicBaseClass<Shape> : PolymorphicDerivedClasses<Circle, Rectangle> {
};
template<>
struct PolymorphicBaseClass<Rectangle> : PolymorphicDerivedClasses<RoundedRectangle> {
};
}
}
// convenient type that stores all our types, so that we could easily register and
// also it automatically ensures, that classes is registered in the same order for serialization and deserialization
using MyPolymorphicClassesForRegistering = bitsery::ext::PolymorphicClassesList<Shape>;
//some helper types
using Buffer = std::vector<uint8_t>;
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<
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 = bitsery::Serializer<Writer, TContext>;
using MyDeserializer = bitsery::Deserializer<Reader, TContext>;
//checks if deserialized data is equal
void assertSameShapes(const SomeShapes &data, const SomeShapes &res) {
{
auto d = dynamic_cast<RoundedRectangle *>(data.uniquePtr.get());
auto r = dynamic_cast<RoundedRectangle *>(res.uniquePtr.get());
assert(r != nullptr);
assert(*d == *r);
}
{
auto d = dynamic_cast<Circle *>(data.sharedList[0].get());
auto r = dynamic_cast<Circle *>(res.sharedList[0].get());
assert(r != nullptr);
assert(*d == *r);
}
{
auto d = dynamic_cast<Rectangle *>(data.sharedList[1].get());
auto r = dynamic_cast<Rectangle *>(res.sharedList[1].get());
assert(r != nullptr);
assert(*d == *r);
}
assert(res.weakPtr.lock().get() == res.sharedList[0].get());
assert(res.refPtr == res.sharedList[1].get());
}
int main() {
auto data = createData();
//create buffer to store data
Buffer buffer{};
size_t writtenSize{};
// we will not use quickSerialization/Deserialization functions to show, that we need to register polymorphic classes, explicitly
{
//STEP 2
// before start serialization/deserialization,
// bind it with base polymorphic types, it will go through all reachable classes that is defined in first step.
// NOTE: you dont need to add Rectangle to reach for RoundedRectangle
TContext ctx{};
std::get<1>(ctx).registerBasesList<MySerializer>(MyPolymorphicClassesForRegistering{});
//create writer and serialize
MySerializer ser{ctx, buffer};
ser.object(data);
ser.adapter().flush();
writtenSize = ser.adapter().writtenBytesCount();
//make sure that pointer linking context is valid
//this ensures that all non-owning pointers points to data that has been serialized,
//so we can successfully reconstruct pointers after deserialization
assert(std::get<0>(ctx).isValid());
}
SomeShapes res{};
{
TContext ctx{};
std::get<1>(ctx).registerBasesList<MyDeserializer>(MyPolymorphicClassesForRegistering{});
//deserialize our data
MyDeserializer des{ctx, buffer.begin(), writtenSize};
des.object(res);
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,
// it is only required if there are any pointers that manage shared state, e.g. std::shared_ptr
assert(res.weakPtr.use_count() == 2);//one in sharedList and one in pointer linking context
std::get<0>(ctx).clearSharedState();
assert(res.weakPtr.use_count() == 1);
}
assertSameShapes(data, res);
return 0;
}

View File

@@ -1,197 +0,0 @@
# Copyright (c) 2012 - 2015, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
# http://stackoverflow.com/a/22404544/80480
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
IF("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
MESSAGE(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
ENDIF()
ELSEIF(NOT CMAKE_COMPILER_IS_GNUCXX)
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
ENDIF() # CHECK VALID COMPILER
SET(CMAKE_CXX_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
SET(coverage_info "${CMAKE_BINARY_DIR}/${_outputname}.info")
SET(coverage_cleaned "${coverage_info}.cleaned")
SEPARATE_ARGUMENTS(test_command UNIX_COMMAND "${_testrunner}")
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${test_command} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${coverage_info}
COMMAND ${LCOV_PATH} --remove ${coverage_info} 'tests/*' '/usr/*' --output-file ${coverage_cleaned}
COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned}
COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests
# Param _outputname cobertura output is generated as _outputname.xml
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
IF(NOT PYTHON_EXECUTABLE)
MESSAGE(FATAL_ERROR "Python not found! Aborting...")
ENDIF() # NOT PYTHON_EXECUTABLE
IF(NOT GCOVR_PATH)
MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
ENDIF() # NOT GCOVR_PATH
ADD_CUSTOM_TARGET(${_targetname}
# Run tests
${_testrunner} ${ARGV3}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -o ${_outputname}.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA

View File

@@ -1,79 +0,0 @@
#MIT License
#
#Copyright (c) 2017 Mindaugas Vinkelis
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.
cmake_minimum_required(VERSION 3.2)
project(gtest_builder C CXX)
include(ExternalProject)
set(GTEST_FORCE_SHARED_CRT ON)
set(GTEST_DISABLE_PTHREADS OFF)
if(MINGW)
set(GTEST_DISABLE_PTHREADS ON)
endif()
if (${USE_GMOCK})
message("use gmock")
set(BUILD_ARGS -DBUILD_GTEST=OFF -DBUILD_GMOCK=ON)
else ()
message("use gtest only")
set(BUILD_ARGS -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF)
endif()
if (WIN32 AND NOT MINGW)
set(BUILD_ARGS ${BUILD_ARGS}
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=DebugLibs
-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=ReleaseLibs)
endif()
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
CMAKE_ARGS ${BUILD_ARGS}
-Dgtest_force_shared_crt=${GTEST_FORCE_SHARED_CRT}
-Dgtest_disable_pthreads=${GTEST_DISABLE_PTHREADS}
PREFIX "${CMAKE_CURRENT_BINARY_DIR}"
# disable update command
UPDATE_COMMAND ""
# disable install step
INSTALL_COMMAND ""
)
#export variables
ExternalProject_Get_Property(googletest source_dir)
ExternalProject_Get_Property(googletest binary_dir)
if (${USE_GMOCK})
# need to include both googletest and googlemock
set(GTEST_INCLUDE_DIRS ${source_dir}/googlemock/include ${source_dir}/googletest/include PARENT_SCOPE)
set(GTEST_LIBS_DIR ${binary_dir}/googlemock PARENT_SCOPE)
set(GTEST_LIBNAME gmock PARENT_SCOPE)
set(GTEST_MAIN_LIBNAME gmock_main PARENT_SCOPE)
set(GTEST_LINK_LIBNAMES gmock_main PARENT_SCOPE)
else()
set(GTEST_INCLUDE_DIRS ${source_dir}/googletest/include PARENT_SCOPE)
set(GTEST_LIBS_DIR ${binary_dir}/googletest PARENT_SCOPE)
set(GTEST_LIBNAME gtest PARENT_SCOPE)
set(GTEST_MAIN_LIBNAME gtest_main PARENT_SCOPE)
# need to include both libs gtest and gtest_main
set(GTEST_LINK_LIBNAMES gtest gtest_main PARENT_SCOPE)
endif()

View File

@@ -1,149 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BUFFER_READER_H
#define BITSERY_BUFFER_READER_H
#include "Common.h"
#include <cassert>
#include <algorithm>
namespace bitsery {
struct BufferReader {
using value_type = uint8_t;
BufferReader(const std::vector<uint8_t> &buf) : _buf{buf}, _pos{std::begin(buf)} {
}
template<size_t SIZE, typename T>
bool readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
return !m_scratch
? directRead(&v, 1)
: readBits(reinterpret_cast<UT &>(v), BITS_SIZE<T>);
}
template<size_t SIZE, typename T>
bool readBuffer(T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
return directRead(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it) {
if (!readBits(reinterpret_cast<UT &>(*it), BITS_SIZE<T>))
return false;
}
}
return true;
}
template<typename T>
bool readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(bitsCount <= BITS_SIZE<T>);
const auto bytesRequired = bitsCount > m_scratchBits
? ((bitsCount - 1 - m_scratchBits) >> 3) + 1u
: 0u;
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesRequired)
return false;
readBitsInternal(v, bitsCount);
return true;
}
bool align() {
if (m_scratchBits) {
SCRATCH_TYPE tmp{};
readBitsInternal(tmp, BITS_SIZE<value_type> - m_scratchBits);
return tmp == 0;
}
return true;
}
bool isCompleted() const {
return _pos == std::end(_buf);
}
private:
const std::vector<value_type> &_buf;
decltype(std::begin(_buf)) _pos;
template<typename T>
bool directRead(T *v, size_t count) {
static_assert(!std::is_const<T>::value, "");
const auto bytesCount = sizeof(T) * count;
if (static_cast<size_t>(std::distance(_pos, std::end(_buf))) < bytesCount)
return false;
std::copy_n(_pos, bytesCount, reinterpret_cast<value_type *>(v));
std::advance(_pos, bytesCount);
return true;
}
template<typename T>
void readBitsInternal(T &v, size_t size) {
auto bitsLeft = size;
T res{};
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, BITS_SIZE<value_type>);
if (m_scratchBits < bits) {
value_type tmp;
std::copy_n(_pos, 1, reinterpret_cast<value_type *>(&tmp));
std::advance(_pos, 1);
m_scratch |= static_cast<SCRATCH_TYPE>(tmp) << m_scratchBits;
m_scratchBits += BITS_SIZE<value_type>;
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<SCRATCH_TYPE>(1) << bits) - 1)) << (size - bitsLeft);
res |= shiftedRes;
m_scratch >>= bits;
m_scratchBits -= bits;
bitsLeft -= bits;
}
v = res;
}
using SCRATCH_TYPE = typename BIGGER_TYPE<value_type>::type;
SCRATCH_TYPE m_scratch{};
size_t m_scratchBits{}; ///< Number of bits currently in the scratch buffer. If the user wants to read more bits than this, we have to go fetch another dword from memory.
};
}
#endif //BITSERY_BUFFER_READER_H

View File

@@ -1,183 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BUFFER_WRITER_H
#define BITSERY_BUFFER_WRITER_H
#include "Common.h"
#include <cassert>
#include <algorithm>
#include <iterator>
namespace bitsery {
struct MeasureSize {
template<size_t SIZE, typename T>
void writeBytes(const T &) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_bitsCount += BITS_SIZE<T>;
}
template<typename T>
void writeBits(const T &, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(bitsCount <= BITS_SIZE<T>);
_bitsCount += bitsCount;
}
template<size_t SIZE, typename T>
void writeBuffer(const T *, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_bitsCount += BITS_SIZE<T> * count;
}
//get size in bytes
size_t getSize() const {
return _bitsCount / 8;
}
private:
size_t _bitsCount{};
};
struct BufferWriter {
using value_type = uint8_t;
BufferWriter(std::vector<uint8_t> &buffer) : _buf{buffer}, _outIt{std::back_inserter(buffer)} {
static_assert(std::is_unsigned<value_type>::value, "");
}
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
directWrite(&v, 1);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBits(reinterpret_cast<const UT &>(v), BITS_SIZE<T>);
}
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
directWrite(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBits(reinterpret_cast<const UT &>(*it), BITS_SIZE<T>);
}
}
template<typename T>
void writeBits(const T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(bitsCount <= BITS_SIZE<T>);
assert(v <= ((1ULL << bitsCount) - 1));
writeBitsInternal(v, bitsCount);
}
void align() {
if (m_scratchBits)
writeBitsInternal(value_type{}, BITS_SIZE<value_type> - m_scratchBits);
}
void flush() {
if (m_scratchBits) {
auto tmp = static_cast<value_type>( m_scratch & bufTypeMask );
directWrite(&tmp, 1);
m_scratch >>= m_scratchBits;
m_scratchBits -= m_scratchBits;
}
}
private:
template<typename T>
void directWrite(const T *v, size_t count) {
const auto bytesSize = sizeof(T) * count;
const auto pos = _buf.size();
_buf.resize(pos + bytesSize);
std::copy_n(reinterpret_cast<const value_type *>(v), bytesSize, _buf.data() + pos);
}
template<typename T>
void writeBitsInternal(const T &v, size_t size) {
auto value = v;
auto bitsLeft = size;
while (bitsLeft > 0) {
auto bits = std::min(bitsLeft, BITS_SIZE<value_type>);
m_scratch |= static_cast<SCRATCH_TYPE>( value ) << m_scratchBits;
m_scratchBits += bits;
if (m_scratchBits >= BITS_SIZE<value_type>) {
auto tmp = static_cast<value_type>(m_scratch & bufTypeMask);
directWrite(&tmp, 1);
m_scratch >>= BITS_SIZE<value_type>;
m_scratchBits -= BITS_SIZE<value_type>;
value >>= BITS_SIZE<value_type>;
}
bitsLeft -= bits;
}
}
void writeBitsInternal(const value_type &v, size_t size) {
if (size > 0) {
m_scratch |= static_cast<SCRATCH_TYPE>( v ) << m_scratchBits;
m_scratchBits += size;
if (m_scratchBits >= BITS_SIZE<value_type>) {
auto tmp = static_cast<value_type>(m_scratch & bufTypeMask);
directWrite(&tmp, 1);
m_scratch >>= BITS_SIZE<value_type>;
m_scratchBits -= BITS_SIZE<value_type>;
}
}
}
const value_type bufTypeMask = 0xFF;
using SCRATCH_TYPE = typename BIGGER_TYPE<value_type>::type;
std::vector<value_type> &_buf;
std::back_insert_iterator<std::vector<value_type>> _outIt;
SCRATCH_TYPE m_scratch{};
size_t m_scratchBits{};
//size_t _bufSize{};
};
}
#endif //BITSERY_BUFFER_WRITER_H

View File

@@ -1,238 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_COMMON_H
#define BITSERY_COMMON_H
#include <stdint.h>
namespace bitsery {
template<typename T>
constexpr size_t BITS_SIZE = sizeof(T) << 3;
template<typename T>
struct BIGGER_TYPE {
};
template<>
struct BIGGER_TYPE<uint8_t> {
typedef uint16_t type;
};
template<>
struct BIGGER_TYPE<uint16_t> {
typedef uint32_t type;
};
template<>
struct BIGGER_TYPE<uint32_t> {
typedef uint64_t type;
};
template<>
struct BIGGER_TYPE<int8_t> {
typedef int16_t type;
};
template<>
struct BIGGER_TYPE<int16_t> {
typedef int32_t type;
};
template<>
struct BIGGER_TYPE<int32_t> {
typedef int64_t type;
};
template<>
struct BIGGER_TYPE<char> {
typedef int16_t type;
};
template<typename T>
constexpr size_t ARITHMETIC_OR_ENUM_SIZE = std::is_arithmetic<T>::value || std::is_enum<T>::value ? sizeof(T) : 0;
template<typename T, typename Enable = void>
struct SAME_SIZE_UNSIGNED_TYPE {
typedef std::make_unsigned_t<T> type;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_enum<T>::value>::type> {
typedef std::make_unsigned_t<std::underlying_type_t<T>> type;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
typedef std::conditional_t<std::is_same<T, float>::value, uint32_t, uint64_t> type;
};
template<typename T>
using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE<T>::type;
template<size_t SIZE>
struct ProcessAnyType {
template<typename S, typename T>
static void serialize(S &s, T &&v) {
s.template value<SIZE>(std::forward<T>(v));
}
};
template<>
struct ProcessAnyType<0> {
template<typename S, typename T>
static void serialize(S &s, T &&v) {
s.object(std::forward<T>(v));
}
};
#define SERIALIZE(ObjectType) \
template <typename S, typename T, typename std::enable_if<std::is_same<T, ObjectType>::value || std::is_same<T, const ObjectType>::value>::type* = nullptr> \
S& serialize(S& s, T& o)
/*
* range functions
*/
template<typename T>
constexpr size_t calcRequiredBits(T min, T max) {
size_t res{};
for (auto diff = max - min; diff > 0; diff >>= 1)
++res;
return res;
}
template<typename T, typename Enable = void>
struct RangeSpec {
constexpr RangeSpec(T minValue, T maxValue)
: min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(min, max)} {
}
const T min;
const T max;
const size_t bitsRequired;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(
static_cast<std::underlying_type_t<T>>(min),
static_cast<std::underlying_type_t<T>>(max))} {
}
const T min;
const T max;
const size_t bitsRequired;
};
//this class is used to make default RangeSpec float specialization always prefer constructor with precision
struct BitsConstraint {
explicit constexpr BitsConstraint(size_t bits) : value{bits} {}
const size_t value;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue, BitsConstraint bits) :
min{minValue},
max{maxValue},
bitsRequired{bits.value} {
}
constexpr RangeSpec(T minValue, T maxValue, T precision) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits<SAME_SIZE_UNSIGNED<T>>({}, ((max - min) / precision))} {
}
const T min;
const T max;
const size_t bitsRequired;
};
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
return !(r.min > v || v > r.max);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
using VT = std::underlying_type_t<T>;
return !(static_cast<VT>(r.min) > static_cast<VT>(v)
|| static_cast<VT>(v) > static_cast<VT>(r.max));
}
/*
* delta functions
*/
class ObjectMemoryPosition {
public:
template<typename T>
ObjectMemoryPosition(const T &oldObj, const T &newObj)
:ObjectMemoryPosition{reinterpret_cast<const char *>(&oldObj), reinterpret_cast<const char *>(&newObj),
sizeof(T)} {
}
template<typename T>
bool isFieldsEquals(const T &newObjField) {
return *getOldObjectField(newObjField) == newObjField;
}
template<typename T>
const T *getOldObjectField(const T &field) {
auto offset = reinterpret_cast<const char *>(&field) - newObj;
return reinterpret_cast<const T *>(oldObj + offset);
}
private:
ObjectMemoryPosition(const char *objOld, const char *objNew, size_t)
: oldObj{objOld},
newObj{objNew} {
}
const char *oldObj;
const char *newObj;
};
}
#endif //BITSERY_COMMON_H

View File

@@ -1,220 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DELTADESERIALIZER_H
#define BITSERY_DELTADESERIALIZER_H
#include <array>
#include <stack>
#include <algorithm>
#include "Deserializer.h"
namespace bitsery {
template<typename Reader, typename TObj>
class DeltaDeserializer {
public:
DeltaDeserializer(Reader &r, const TObj &oldObj, const TObj &newObj)
: _deserializer{r},
_reader{r},
_oldObj{oldObj},
_newObj{newObj},
_objMemPos(std::deque<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
_isNewElement{false} {
};
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
DeltaDeserializer &value(T &v) {
if (getChangedState(v)) {
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
_reader.template readBytes<ValueSize>(v);
}
return *this;
}
template<typename T>
DeltaDeserializer &object(T &&obj) {
if (getChangedState(obj))
return serialize(*this, std::forward<T>(obj));
return *this;
}
template<size_t VSIZE = 1, typename T>
DeltaDeserializer &text(std::basic_string<T> &str, size_t maxSize) {
if (getChangedState(str)) {
_deserializer.template text<VSIZE>(str, maxSize);
}
return *this;
}
template<size_t VSIZE = 1, typename T, size_t N>
DeltaDeserializer &text(T (&str)[N]) {
if (getChangedState(str)) {
_deserializer.template text<VSIZE>(str);
}
return *this;
}
template<typename T, size_t N, typename Fnc>
DeltaDeserializer &array(std::array<T, N> &arr, Fnc &&fnc) {
if (getChangedState(arr)) {
if (!_isNewElement) {
const auto old = *_objMemPos.top().getOldObjectField(arr);
processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc);
} else {
for (auto &v:arr)
fnc(v);
}
}
return *this;
}
template<typename T, size_t N, typename Fnc>
DeltaDeserializer &array(T (&arr)[N], Fnc &&fnc) {
if (getChangedState(arr)) {
if (!_isNewElement) {
const auto old = *_objMemPos.top().getOldObjectField(arr);
T *tmp = arr;
processContainer(old, old + N, tmp, tmp + N, fnc);
} else {
T *tmp = arr;
for (auto i = 0u; i < N; ++i, ++tmp)
fnc(*tmp);
}
}
return *this;
}
template<typename T, typename Fnc>
DeltaDeserializer &container(T &obj, Fnc &&fnc, size_t maxSize) {
if (getChangedState(obj)) {
size_t newSize{};
_reader.readBits(newSize, 32);
if (!_isNewElement) {
auto old = *_objMemPos.top().getOldObjectField(obj);
if (old.size() != newSize)
obj.resize(newSize);
processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj),
std::forward<Fnc>(fnc));
} else {
obj.resize(newSize);
for (auto &v:obj)
fnc(v);
}
}
return *this;
}
private:
Deserializer <Reader> _deserializer;
Reader &_reader;
const TObj &_oldObj;
const TObj &_newObj;
std::stack<ObjectMemoryPosition> _objMemPos;
bool _isNewElement;
template<typename T>
bool getChangedState(T &obj) {
if (!_isNewElement) {
if (!readChangedState()) {
obj = *_objMemPos.top().getOldObjectField(obj);
return false;
}
}
return true;
}
template<typename T, size_t N>
bool getChangedState(T (&arr)[N]) {
if (!_isNewElement) {
if (!readChangedState()) {
auto old = *_objMemPos.top().getOldObjectField(arr);
auto end = arr + N;
auto pOld = old;
for (auto p = arr; p != end; ++p, ++pOld)
*p = *pOld;
return false;
}
}
return true;
}
template<typename TConstIt, typename TIt, typename Fnc>
bool processContainer(TConstIt oldBegin, TConstIt oldEnd, TIt begin, TIt end, Fnc &&fnc) {
auto offset = readIndexOffset();
auto p = begin;
auto pOld = oldBegin;
for (; p != end && pOld != oldEnd; ++p, ++pOld) {
if (offset) {
*p = *pOld;
--offset;
} else {
_objMemPos.emplace(ObjectMemoryPosition{*pOld, *p});
fnc(*p);
_objMemPos.pop();
offset = readIndexOffset();
}
}
if (offset != 0 && pOld != oldEnd)
return false;
_isNewElement = true;
for (; p != end; ++p, --offset)
fnc(*p);
_isNewElement = false;
return offset == 0;
}
bool readChangedState() {
unsigned char res{};
_reader.readBits(res, 1);
return res;
}
size_t readIndexOffset() {
//special case, if items are updated sequentialy
unsigned char tmp{};
_reader.readBits(tmp, 1);
if (tmp) {
return 0u;
} else {
size_t res{};
_reader.readBits(tmp, 1);
if (tmp > 0)
_reader.readBits(res, 4);
else
_reader.readBits(res, 32);
return res;
}
}
};
}
#endif //BITSERY_DELTADESERIALIZER_H

View File

@@ -1,220 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DELTASERIALIZER_H
#define BITSERY_DELTASERIALIZER_H
#include <array>
#include <stack>
#include <algorithm>
#include "Serializer.h"
namespace bitsery {
template<typename Writter, typename TObj>
class DeltaSerializer {
public:
DeltaSerializer(Writter &w, const TObj &oldObj, const TObj &newObj)
: _serializer{w},
_writter{w},
_oldObj{oldObj},
_newObj{newObj},
_objMemPos(std::deque<ObjectMemoryPosition>(1, ObjectMemoryPosition{oldObj, newObj})),
_isNewElement{false} {
};
template<size_t SIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
DeltaSerializer &value(const T &v) {
if (setChangedState(v)) {
constexpr size_t ValueSize = SIZE == 0 ? sizeof(T) : SIZE;
_writter.template writeBytes<ValueSize>(v);
}
return *this;
}
template<typename T>
DeltaSerializer &object(T &&obj) {
if (setChangedState(obj)) {
serialize(*this, std::forward<T>(obj));
}
return *this;
}
template<size_t VSIZE = 1, typename T>
DeltaSerializer &text(const std::basic_string<T> &str, size_t maxSize) {
if (setChangedState(str)) {
_serializer.template text<VSIZE>(str, maxSize);
}
return *this;
}
template<size_t VSIZE = 1, typename T, size_t N>
DeltaSerializer &text(const T (&str)[N]) {
if (setChangedState(str)) {
_serializer.template text<VSIZE>(str);
}
return *this;
}
template<typename T, size_t N, typename Fnc>
DeltaSerializer &array(const std::array<T, N> &arr, Fnc &&fnc) {
if (setChangedState(arr)) {
if (!_isNewElement) {
const auto &old = *_objMemPos.top().getOldObjectField(arr);
processContainer(std::begin(old), std::end(old), std::begin(arr), std::end(arr), fnc);
} else {
for (auto &v:arr)
fnc(v);
}
}
return *this;
}
template<typename T, size_t N, typename Fnc>
DeltaSerializer &array(const T (&arr)[N], Fnc &&fnc) {
if (setChangedState(arr)) {
if (!_isNewElement) {
auto old = *_objMemPos.top().getOldObjectField(arr);
const T *tmp = arr;
processContainer(old, old + N, tmp, tmp + N, fnc);
} else {
const T *tmp = arr;
for (auto i = 0u; i < N; ++i, ++tmp)
fnc(*tmp);
}
}
return *this;
}
template<typename T, typename Fnc>
DeltaSerializer &container(T &&obj, Fnc &&fnc, size_t maxSize) {
if (setChangedState(obj)) {
_writter.writeBits(obj.size(), 32);
if (!_isNewElement) {
auto old = *_objMemPos.top().getOldObjectField(obj);
processContainer(std::begin(old), std::end(old), std::begin(obj), std::end(obj),
std::forward<Fnc>(fnc));
} else {
for (auto &v:obj)
fnc(v);
}
}
return *this;
}
private:
Serializer <Writter> _serializer;
Writter &_writter;
const TObj &_oldObj;
const TObj &_newObj;
std::stack<ObjectMemoryPosition> _objMemPos;
bool _isNewElement;
template<typename T>
bool setChangedState(const T &obj) {
if (!_isNewElement) {
auto res = !_objMemPos.top().isFieldsEquals(obj);
writeChangedState(res);
return res;
}
return true;
}
template<typename T, size_t N>
bool setChangedState(const T (&arr)[N]) {
if (!_isNewElement) {
auto old = *_objMemPos.top().getOldObjectField(arr);
auto end = arr + N;
bool changed{};
for (auto p = arr, pOld = old; p != end; ++p, ++pOld) {
if (!(*p == *pOld)) {
changed = true;
break;
}
}
writeChangedState(changed);
return changed;
}
return true;
}
template<typename T, typename Fnc>
void processContainer(const T oldBegin, const T oldEnd, const T begin, const T end, Fnc &&fnc) {
auto misMatch = std::mismatch(oldBegin, oldEnd, begin, end);
auto lastChanged = begin;
while (misMatch.first != oldEnd && misMatch.second != end) {
writeIndexOffset(std::distance(lastChanged, misMatch.second));
_objMemPos.emplace(ObjectMemoryPosition{*misMatch.first, *misMatch.second});
fnc(*misMatch.second);
_objMemPos.pop();
++misMatch.first;
++misMatch.second;
lastChanged = misMatch.second;
misMatch = std::mismatch(misMatch.first, oldEnd, misMatch.second, end);
}
auto p = misMatch.second;
//write items left
writeIndexOffset(std::distance(lastChanged, end));
//write old elements
for (auto pOld = misMatch.first; p != end && pOld != oldEnd; ++p, ++pOld) {
_objMemPos.emplace(ObjectMemoryPosition{*pOld, *p});
fnc(*p);
_objMemPos.pop();
}
//write new elements
_isNewElement = true;
for (; p != end; ++p)
fnc(*p);
_isNewElement = false;
}
void writeChangedState(bool state) {
_writter.writeBits(state ? 1u : 0u, 1);
}
void writeIndexOffset(const size_t offset) {
//special case, if items are updated sequentialy
if (offset == 0) {
_writter.writeBits(1u, 1);
} else {
_writter.writeBits(0u, 1);
auto smallOffset = offset < 16;
_writter.writeBits(smallOffset ? 1u : 0u, 1);
if (smallOffset)
_writter.writeBits(offset, 4);
else
_writter.writeBits(offset, 32);
}
}
};
}
#endif //BITSERY_DELTASERIALIZER_H

View File

@@ -1,357 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DESERIALIZER_H
#define BITSERY_DESERIALIZER_H
#include "Common.h"
#include <array>
#include <utility>
namespace bitsery {
/*
* functions for range
*/
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
v += r.min;
};
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
using VT = std::underlying_type_t<T>;
reinterpret_cast<VT&>(v) += static_cast<VT>(r.min);
};
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
using UIT = SAME_SIZE_UNSIGNED<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);
};
template<typename Reader>
class Deserializer {
public:
Deserializer(Reader& r):_reader{r}, _isValid{true} {};
template <typename T>
Deserializer& object(T&& obj) {
return serialize(*this, std::forward<T>(obj));
}
/*
* value overloads
*/
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
Deserializer& value(T& v) {
static_assert(std::numeric_limits<float>::is_iec559, "");
static_assert(std::numeric_limits<double>::is_iec559, "");
if (_isValid) {
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_isValid = _reader.template readBytes<ValueSize>(reinterpret_cast<SAME_SIZE_UNSIGNED<T>&>(v));
}
return *this;
}
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
Deserializer& value(T& v) {
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
using UT = std::underlying_type_t<T>;
if (_isValid) {
_isValid = _reader.template readBytes<ValueSize>(reinterpret_cast<UT&>(v));
}
return *this;
}
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
Deserializer& value(T& v) {
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
if (_isValid) {
_isValid = _reader.template readBytes<ValueSize>(v);
}
return *this;
}
/*
* bool
*/
Deserializer& boolBit(bool& v) {
if (_isValid) {
unsigned char tmp;
_isValid = _reader.readBits(tmp, 1);
v = tmp == 1;
}
return *this;
}
Deserializer& boolByte(bool& v) {
if (_isValid) {
unsigned char tmp;
_isValid = _reader.template readBytes<1>(tmp);
if (_isValid)
_isValid = tmp < 2;
v = tmp == 1;
}
return *this;
}
/*
* range
*/
template <typename T>
Deserializer& range(T& v, const RangeSpec<T>& range) {
if (_isValid) {
_isValid = _reader.template readBits(reinterpret_cast<SAME_SIZE_UNSIGNED<T>&>(v), range.bitsRequired);
setRangeValue(v, range);
if (_isValid)
_isValid = isRangeValid(v, range);
}
return *this;
}
/*
* substitution overloads
*/
template<typename T, size_t N, typename Fnc>
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues, Fnc&& fnc) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
if (index)
v = expectedValues[index-1];
else
fnc(v);
}
return *this;
};
template<size_t VSIZE, typename T, size_t N>
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
if (index)
v = expectedValues[index-1];
else
ProcessAnyType<VSIZE>::serialize(*this, v);
}
return *this;
};
template<typename T, size_t N>
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
if (index)
v = expectedValues[index-1];
else
ProcessAnyType<ARITHMETIC_OR_ENUM_SIZE<T>>::serialize(*this, v);
}
return *this;
};
/*
* text overloads
*/
template <size_t VSIZE = 1, typename T>
Deserializer& text(std::basic_string<T>& str, size_t maxSize) {
size_t size;
readSize(size, maxSize);
if (_isValid) {
str.resize(size);
if (size) {
//if (std::is_const<decltype(std::declval<std::basic_string<T>>().data())>::value) {
std::vector<T> buf(size);
_isValid = _reader.template readBuffer<VSIZE>(buf.data(), size);
str.assign(buf.data(), size);
//} else {
//_isValid = _reader.template readBuffer<VSIZE>(str.data(), size);
//}
}
}
return *this;
}
template<size_t VSIZE=1, typename T, size_t N>
Deserializer& text(T (&str)[N]) {
size_t size;
readSize(size, N-1);
if (_isValid) {
_isValid = _reader.template readBuffer<VSIZE>(str, size);
str[size] = {};
}
return *this;
}
/*
* container overloads
*/
template <typename T, typename Fnc>
Deserializer& container(T&& obj, Fnc&& fnc, size_t maxSize) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
for (auto& v:obj) {
if (_isValid)
fnc(v);
}
}
return *this;
}
template <size_t VSIZE, typename T>
Deserializer& container(T& obj, size_t maxSize) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
procContainer<VSIZE>(obj);
}
return *this;
}
template <typename T>
Deserializer& container(T& obj, size_t maxSize) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
procContainer<ARITHMETIC_OR_ENUM_SIZE<typename T::value_type>>(obj);
}
return *this;
}
/*
* array overloads (fixed size array (std::array, and c-style array))
*/
//std::array overloads
template<typename T, size_t N, typename Fnc>
Deserializer& array(std::array<T,N> &arr, Fnc && fnc) {
for (auto& v: arr)
if (_isValid)
fnc(v);
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Deserializer& array(std::array<T,N> &arr) {
procContainer<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Deserializer& array(std::array<T,N> &arr) {
procContainer<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
Deserializer& array(T (&arr)[N], Fnc&& fnc) {
T* tmp = arr;
for (auto i = 0u; i < N; ++i, ++tmp)
if (_isValid)
fnc(*tmp);
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Deserializer& array(T (&arr)[N]) {
procCArray<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Deserializer& array(T (&arr)[N]) {
procCArray<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
bool isValid() const {
return _isValid;
}
private:
Reader& _reader;
bool _isValid;
void readSize(size_t &size, size_t maxSize) {
size = {};
if (_isValid) {
unsigned char firstBit;
_isValid = _reader.readBits(firstBit, 1);
if (_isValid) {
if (firstBit) {
_isValid = _reader.readBits(size, 7);
} else {
unsigned char secondBit;
_isValid = _reader.readBits(secondBit, 1);
if (_isValid) {
if (secondBit) {
_isValid = _reader.readBits(size,14);
} else {
_isValid = _reader.readBits(size,30);
}
}
}
}
if (_isValid)
_isValid = size <= maxSize;
}
}
template <size_t VSIZE, typename T>
void procContainer(T&& obj) {
//todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector<bool> specialization)
for (auto& v: obj)
if (_isValid)
ProcessAnyType<VSIZE>::serialize(*this, v);
};
template <size_t VSIZE, typename T, size_t N>
void procCArray(T (&arr)[N]) {
//todo could be improved for arithmetic types
T* end = arr + N;
for (T* it = arr; it != end; ++it)
if (_isValid)
ProcessAnyType<VSIZE>::serialize(*this, *it);
};
};
}
#endif //BITSERY_DESERIALIZER_H

View File

@@ -1,300 +0,0 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_SERIALIZER_H
#define BITSERY_SERIALIZER_H
#include "Common.h"
#include <array>
namespace bitsery {
/*
* functions for range
*/
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
auto getRangeValue(const T &v, const RangeSpec<T> &r) {
return static_cast<SAME_SIZE_UNSIGNED<T>>(v - r.min);
};
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
auto getRangeValue(const T &v, const RangeSpec<T> &r) {
return static_cast<SAME_SIZE_UNSIGNED<T>>(v) - static_cast<SAME_SIZE_UNSIGNED<T>>(r.min);
};
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
auto getRangeValue(const T &v, const RangeSpec<T> &r) {
using VT = SAME_SIZE_UNSIGNED<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);
};
/*
* functions for substitution
*/
template<typename T, size_t N>
size_t findSubstitutionIndex(const T &v, const std::array<T, N> &defValues) {
auto index{1u};
for (auto &d:defValues) {
if (d == v)
return index;
++index;
}
return 0u;
};
template<typename Writter>
class Serializer {
public:
Serializer(Writter &w) : _writter{w} {};
template<typename T>
Serializer& object(const T &obj) {
return serialize(*this, obj);
}
/*
* value overloads
*/
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
Serializer& value(const T &v) {
static_assert(std::numeric_limits<float>::is_iec559, "");
static_assert(std::numeric_limits<double>::is_iec559, "");
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_writter.template writeBytes<ValueSize>(reinterpret_cast<const SAME_SIZE_UNSIGNED<T> &>(v));
return *this;
}
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
Serializer& value(const T &v) {
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_writter.template writeBytes<ValueSize>(reinterpret_cast<const std::underlying_type_t<T> &>(v));
return *this;
}
template<size_t VSIZE = 0, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
Serializer& value(const T &v) {
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_writter.template writeBytes<ValueSize>(v);
return *this;
}
/*
* bool
*/
Serializer& boolBit(bool v) {
_writter.template writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
return *this;
}
Serializer& boolByte(bool v) {
_writter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
return *this;
}
/*
* range
*/
template<typename T>
Serializer& range(const T &v, const RangeSpec<T> &range) {
assert(isRangeValid(v, range));
_writter.template writeBits(getRangeValue(v, range), range.bitsRequired);
return *this;
}
/*
* substitution overloads
*/
template<typename T, size_t N, typename Fnc>
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
auto index = findSubstitutionIndex(v, expectedValues);
range(index, {{}, N +1});
if (!index)
fnc(v);
return *this;
};
template<size_t VSIZE, typename T, size_t N>
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues) {
auto index = findSubstitutionIndex(v, expectedValues);
range(index, {{}, N +1});
if (!index)
ProcessAnyType<VSIZE>::serialize(*this, v);
return *this;
};
template<typename T, size_t N>
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues) {
auto index = findSubstitutionIndex(v, expectedValues);
range(index, {{}, N +1});
if (!index)
ProcessAnyType<ARITHMETIC_OR_ENUM_SIZE<T>>::serialize(*this, v);
return *this;
};
/*
* text overloads
*/
template<size_t VSIZE = 1, typename T>
Serializer& text(const std::basic_string<T> &str, size_t maxSize) {
assert(str.size() <= maxSize);
procText<VSIZE>(str.data(), str.size());
return *this;
}
template<size_t VSIZE = 1, typename T, size_t N>
Serializer& text(const T (&str)[N]) {
procText<VSIZE>(str, std::min(std::char_traits<T>::length(str), N - 1));
return *this;
}
/*
* container overloads
*/
template<typename T, typename Fnc>
Serializer& container(const T &obj, Fnc &&fnc, size_t maxSize) {
assert(obj.size() <= maxSize);
writeSize(obj.size());
for (auto &v: obj)
fnc(v);
return *this;
}
template<size_t VSIZE, typename T>
Serializer& container(const T &obj, size_t maxSize) {
assert(obj.size() <= maxSize);
writeSize(obj.size());
procContainer<VSIZE>(obj);
return *this;
}
template<typename T>
Serializer& container(const T &obj, size_t maxSize) {
assert(obj.size() <= maxSize);
writeSize(obj.size());
procContainer<ARITHMETIC_OR_ENUM_SIZE<typename T::value_type>>(obj);
return *this;
}
/*
* array overloads (fixed size array (std::array, and c-style array))
*/
//std::array overloads
template<typename T, size_t N, typename Fnc>
Serializer& array(const std::array<T, N> &arr, Fnc &&fnc) {
for (auto &v: arr)
fnc(v);
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Serializer& array(const std::array<T, N> &arr) {
procContainer<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Serializer& array(const std::array<T, N> &arr) {
procContainer<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
Serializer& array(const T (&arr)[N], Fnc &&fnc) {
const T *end = arr + N;
for (const T *tmp = arr; tmp != end; ++tmp)
fnc(*tmp);
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Serializer& array(const T (&arr)[N]) {
procCArray<VSIZE>(arr);
return *this;
}
template<typename T, size_t N>
Serializer& array(const T (&arr)[N]) {
procCArray<ARITHMETIC_OR_ENUM_SIZE<T>>(arr);
return *this;
}
private:
Writter &_writter;
void writeSize(const size_t size) {
if (size < 0x80u) {
_writter.writeBits(1u, 1);
_writter.writeBits(size, 7);
} else if (size < 0x4000u) {
_writter.writeBits(2u,2);
_writter.writeBits(size, 14);
} else {
assert(size < 0x40000000u);
_writter.writeBits(0u,2);
_writter.writeBits(size, 30);
}
}
template<size_t VSIZE, typename T>
void procContainer(T &&obj) {
//todo could be improved for arithmetic types in contiguous containers (std::vector, std::array) (keep in mind std::vector<bool> specialization)
for (auto &v: obj)
ProcessAnyType<VSIZE>::serialize(*this, v);
};
template<size_t VSIZE, typename T, size_t N>
void procCArray(T (&arr)[N]) {
//todo could be improved for arithmetic types
const T *end = arr + N;
for (const T *it = arr; it != end; ++it)
ProcessAnyType<VSIZE>::serialize(*this, *it);
};
template<size_t VSIZE, typename T>
void procText(const T *str, size_t size) {
writeSize(size);
if (size)
_writter.template writeBuffer<VSIZE>(str, size);
}
};
}
#endif //BITSERY_SERIALIZER_H

View File

@@ -0,0 +1,332 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_BUFFER_H
#define BITSERY_ADAPTER_BUFFER_H
#include "../details/adapter_common.h"
#include "../traits/core/traits.h"
namespace bitsery {
template<typename Buffer, typename Config = DefaultConfig>
class InputBufferAdapter: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
public:
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>>;
using TConfig = Config;
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 beginIt, size_t size)
: _beginIt{beginIt},
_currOffset{0},
_endReadOffset{size},
_bufferSize{size} {
};
InputBufferAdapter(TIterator beginIt, TIterator endIt)
:InputBufferAdapter(beginIt, static_cast<size_t>(std::distance(beginIt, endIt))) {
}
InputBufferAdapter(const InputBufferAdapter&) = delete;
InputBufferAdapter& operator=(const InputBufferAdapter&) = delete;
InputBufferAdapter(InputBufferAdapter&&) = default;
InputBufferAdapter& operator = (InputBufferAdapter&&) = default;
void currentReadPos(size_t pos) {
currentReadPosChecked(pos, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
size_t currentReadPos() const {
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.");
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_overflowOnReadEndPos = pos == 0;
if (pos == 0)
pos = _bufferSize;
_endReadOffset = pos;
} else {
error(ReaderError::DataOverflow);
}
}
size_t currentReadEndPos() const {
if (_overflowOnReadEndPos)
return 0;
return _endReadOffset;
}
ReaderError error() const {
return _currOffset <= _endReadOffset
? ReaderError::NoError
: static_cast<ReaderError>(_currOffset - _endReadOffset);
}
void error(ReaderError error) {
if (_currOffset <= _endReadOffset) {
_endReadOffset = 0;
_bufferSize = 0;
_currOffset = static_cast<size_t>(error);
}
}
bool isCompletedSuccessfully() const {
return _currOffset == _bufferSize;
}
private:
using diff_t = typename std::iterator_traits<TIterator>::difference_type;
template <size_t SIZE>
void readInternalValue(TValue *data) {
readInternalValueChecked<SIZE>(data, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
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 {
//set everything to zeros
std::memset(data, 0, size);
if (_overflowOnReadEndPos)
error(ReaderError::DataOverflow);
}
}
void currentReadPosChecked(size_t pos, std::true_type) {
if (_bufferSize >= pos && error() == ReaderError::NoError) {
_currOffset = pos;
} else {
error(ReaderError::DataOverflow);
}
}
void currentReadPosChecked(size_t pos, std::false_type) {
_currOffset = pos;
}
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;
};
template<typename Buffer, typename Config = DefaultConfig>
class OutputBufferAdapter: public details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>> {
public:
friend details::OutputAdapterBaseCRTP<OutputBufferAdapter<Buffer,Config>>;
using TConfig = Config;
using TIterator = typename traits::BufferAdapterTraits<Buffer>::TIterator;
using TValue = typename traits::BufferAdapterTraits<Buffer>::TValue;
static_assert(details::IsDefined<TValue>::value,
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<Buffer>::isContiguous,
"BufferAdapter only works with contiguous containers");
OutputBufferAdapter(Buffer &buffer)
: _buffer{std::addressof(buffer)},
_beginIt{std::begin(buffer)} {
init(TResizable{});
}
OutputBufferAdapter(const OutputBufferAdapter&) = delete;
OutputBufferAdapter& operator=(const OutputBufferAdapter&) = delete;
OutputBufferAdapter(OutputBufferAdapter&&) = default;
OutputBufferAdapter& operator = (OutputBufferAdapter&&) = default;
void currentWritePos(size_t pos) {
const auto maxPos = _currOffset > pos ? _currOffset : pos;
if (maxPos > _biggestCurrentPos) {
_biggestCurrentPos = maxPos;
}
setCurrentWritePos(pos, TResizable{});
}
size_t currentWritePos() const {
return _currOffset;
}
void flush() {
//this function might be useful for stream adapters
}
size_t writtenBytesCount() const {
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;
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 _beginIt;
size_t _currOffset{0};
size_t _bufferSize{0};
size_t _biggestCurrentPos{0};
/*
* resizable buffer
*/
void init(std::true_type) {
//resize buffer immediately, because we need output iterator at valid position
if (traits::ContainerTraits<Buffer>::size(*_buffer) == 0u) {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
}
updateIteratorAndSize();
}
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 {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
updateIteratorAndSize();
writeInternalValueImpl<SIZE>(data, 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) {
if (pos <= _bufferSize) {
_currOffset = pos;
} else {
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
updateIteratorAndSize();
setCurrentWritePos(pos, std::true_type{});
}
}
/*
* non resizable buffer
*/
void init(std::false_type) {
updateIteratorAndSize();
}
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) {
assert(pos <= _bufferSize);
_currOffset = pos;
}
void updateIteratorAndSize() {
_beginIt = std::begin(*_buffer);
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
}
};
}
#endif //BITSERY_ADAPTER_BUFFER_H

View File

@@ -0,0 +1,95 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_MEASURE_SIZE_H
#define BITSERY_ADAPTER_MEASURE_SIZE_H
namespace bitsery {
template<typename Config>
class BasicMeasureSize {
public:
static constexpr bool BitPackingEnabled = true;
using TConfig = Config;
using TValue = void;
template<size_t SIZE, typename T>
void writeBytes(const T&) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPosBits += details::BitsSize<T>::value;
}
template<size_t SIZE, typename T>
void writeBuffer(const T*, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
_currPosBits += details::BitsSize<T>::value * count;
}
template<typename T>
void writeBits(const T&, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(bitsCount <= details::BitsSize<T>::value);
_currPosBits += bitsCount;
}
void currentWritePos(size_t pos) {
align();
const auto newPos = pos * 8;
if (_currPosBits > newPos)
_prevLargestPos = _currPosBits;
_currPosBits = newPos;
}
size_t currentWritePos() const {
return _currPosBits / 8;
}
void align() {
auto _scratch = (_currPosBits % 8);
_currPosBits += (8 - _scratch) % 8;
}
void flush() {
align();
}
//get size in bytes
size_t writtenBytesCount() const {
const auto max = _currPosBits > _prevLargestPos ? _currPosBits : _prevLargestPos;
return max / 8;
}
private:
size_t _prevLargestPos{};
size_t _currPosBits{};
};
//helper type for default config
using MeasureSize = BasicMeasureSize<DefaultConfig>;
}
#endif //BITSERY_ADAPTER_MEASURE_SIZE_H

View File

@@ -0,0 +1,297 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_STREAM_H
#define BITSERY_ADAPTER_STREAM_H
#include "../details/adapter_common.h"
#include "../traits/array.h"
#include <ios>
namespace bitsery {
template <typename TChar, typename Config, typename CharTraits>
class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
:_ios{std::addressof(istream)} {}
BasicInputStreamAdapter(const BasicInputStreamAdapter&) = delete;
BasicInputStreamAdapter& operator = (const BasicInputStreamAdapter&) = delete;
BasicInputStreamAdapter(BasicInputStreamAdapter&&) = default;
BasicInputStreamAdapter& operator = (BasicInputStreamAdapter&&) = default;
void currentReadPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
}
size_t currentReadPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
return {};
}
void currentReadEndPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
}
size_t currentReadEndPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
return {};
}
ReaderError error() const {
return _err;
}
bool isCompletedSuccessfully() const {
if (error() == ReaderError::NoError) {
return _ios->rdbuf()->sgetc() == CharTraits::eof();
}
return false;
}
void error(ReaderError error) {
if (_err == ReaderError::NoError) {
_err = error;
_zeroIfNoErrors = std::numeric_limits<size_t>::max();
}
}
private:
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, static_cast<std::streamsize>(size))) != _zeroIfNoErrors) {
*data = {};
if (_zeroIfNoErrors == 0) {
error(_ios->rdstate() == std::ios_base::badbit
? ReaderError::ReadingError
: ReaderError::DataOverflow);
}
}
}
void readChecked(TValue* data, size_t size, std::false_type) {
_ios->rdbuf()->sgetn(data , static_cast<std::streamsize>(size));
}
std::basic_ios<TChar, CharTraits>* _ios;
size_t _zeroIfNoErrors{};
ReaderError _err = ReaderError::NoError;
};
template <typename TChar, typename Config, typename CharTraits>
class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
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");
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
_ostream->flush();
}
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
//streaming doesn't return written bytes
return 0u;
}
private:
template <size_t SIZE>
void writeInternalValue(const TValue* data) {
_ostream->rdbuf()->sputn( data , SIZE );
}
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>>
class BasicBufferedOutputStreamAdapter:
public details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>> {
public:
friend details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using TConfig = Config;
using Buffer = TBuffer;
using BufferIt = typename traits::BufferAdapterTraits<TBuffer>::TIterator;
static_assert(details::IsDefined<BufferIt>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer for BasicBufferedOutputStreamAdapter");
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BasicBufferedOutputStreamAdapter only works with contiguous containers");
using TValue = TChar;
//bufferSize is used when buffer is dynamically allocated
BasicBufferedOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream, size_t bufferSize = 256)
:_ostream(std::addressof(ostream)),
_buf{},
_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
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
: _ostream{rhs._ostream},
_buf{std::move(rhs._buf)},
_beginIt{std::begin(_buf)},
_currOffset{rhs._currOffset},
_bufferSize{rhs._bufferSize}
{
};
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
_ostream = rhs._ostream;
_buf = std::move(rhs._buf);
_beginIt = std::begin(_buf);
_currOffset = rhs._currOffset;
_bufferSize = rhs._bufferSize;
return *this;
};
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
writeBufferToStream();
_ostream->flush();
}
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
//streaming doesn't return written bytes
return 0u;
}
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
using diff_t = typename std::iterator_traits<BufferIt>::difference_type;
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;
}
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 writeBufferToStream() {
_ostream->rdbuf()->sputn(std::addressof(*_beginIt), static_cast<std::streamsize>(_currOffset));
_currOffset = 0;
}
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) {
// ignore buffer size parameter, and instead take actual buffer size
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
}
std::basic_ostream<TChar, CharTraits>* _ostream;
TBuffer _buf;
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>
{
public:
using TValue = TChar;
//both bases contain reference to same iostream, so no need to do anything
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 OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
}
#endif //BITSERY_ADAPTER_STREAM_H

View File

@@ -24,8 +24,8 @@
#ifndef BITSERY_BITSERY_H
#define BITSERY_BITSERY_H
#define BITSERY_MAJOR_VERSION 1
#define BITSERY_MINOR_VERSION 0
#define BITSERY_MAJOR_VERSION 5
#define BITSERY_MINOR_VERSION 1
#define BITSERY_PATCH_VERSION 0
#define BITSERY_QUOTE_MACRO(name) #name
@@ -37,10 +37,7 @@ BITSERY_QUOTE_MACRO(patch)
#define BITSERY_VERSION \
BITSERY_BUILD_VERSION_STR(BITSERY_MAJOR_VERSION, BITSERY_MINOR_VERSION, BITSERY_PATCH_VERSION)
#include "BufferWriter.h"
#include "BufferReader.h"
#include "Serializer.h"
#include "Deserializer.h"
#include "serializer.h"
#include "deserializer.h"
#endif //BITSERY_BITSERY_H

View File

@@ -0,0 +1,94 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_H
#define BITSERY_BRIEF_SYNTAX_H
#include "details/serialization_common.h"
#include "details/brief_syntax_common.h"
namespace bitsery {
//define function that enables s(....) usage
template<typename S, typename T>
void processBriefSyntax(S& s, T&& head) {
static_assert(std::is_lvalue_reference<T>::value || std::is_base_of<brief_syntax::ModFnc, T>::value,
"Argument must be either lvalue or subclass of brief_syntax::ModFnc");
s.object(head);
}
//wrapper functions that enables to serialize as container or string
template<typename T, size_t N>
brief_syntax::CArray<T, N, true> asText(T (& str)[N]) {
return {str};
}
template<typename T, size_t N>
brief_syntax::CArray<T, N, false> asContainer(T (& obj)[N]) {
return {obj};
}
template<typename T>
brief_syntax::MaxSize<T> maxSize(T& obj, size_t max) {
return {obj, max};
}
//define serialize function for fundamental types
template<typename S>
void serialize(S& s, bool& v) {
s.boolValue(v);
}
template<typename S, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
void serialize(S& s, T& v) {
s.template value<sizeof(T)>(v);
}
//define serialization for c-style container
//if array is integral type, specify explicitly how to process: as text or container
template<typename S, typename T, size_t N, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void serialize(S&, T (&)[N]) {
static_assert(N == 0,
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using c-style array with integral types\n");
}
template<typename S, typename T, size_t N, typename std::enable_if<!std::is_integral<T>::value>::type * = nullptr>
void serialize(S& s, T (& obj)[N]) {
brief_syntax::processContainer(s, obj);
}
//this is a helper class that enforce fundamental type sizes, when used on multiple platforms
template<size_t TShort, size_t TInt, size_t TLong, size_t TLongLong>
void assertFundamentalTypeSizes() {
//http://en.cppreference.com/w/cpp/language/types
static_assert(sizeof(short) == TShort, "");
static_assert(sizeof(int) == TInt, "");
static_assert(sizeof(long) == TLong, "");
static_assert(sizeof(long long) == TLongLong, "");
//for completion we also need pointer type size, but serializer doesn't support pointer serialization.
}
}
#endif //BITSERY_BRIEF_SYNTAX_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H
#include "../traits/array.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, size_t N>
void serialize(S &s, std::array<T, N> &obj) {
brief_syntax::processContainer(s, obj);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_ARRAY_H

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

@@ -0,0 +1,40 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H
#include "../ext/std_chrono.h"
namespace bitsery {
template<typename S, typename T, typename P>
void serialize(S &s, std::chrono::duration<T, P> &obj) {
s.template ext<sizeof(T)>(obj, ext::StdDuration{});
}
template<typename S, typename C, typename T, typename P>
void serialize(S &s, std::chrono::time_point<C, std::chrono::duration<T, P>> &obj) {
s.template ext<sizeof(T)>(obj, ext::StdTimePoint{});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_CHRONO_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H
#include "../traits/deque.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::deque<T, Allocator> &obj) {
brief_syntax::processContainer(s, obj);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_DEQUE_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H
#include "../traits/forward_list.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::forward_list<T, Allocator> &obj) {
brief_syntax::processContainer(s, obj);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_FORWARD_LIST_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H
#include "../traits/list.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::list<T, Allocator> &obj) {
brief_syntax::processContainer(s, obj);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_LIST_H

View File

@@ -0,0 +1,50 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H
#include <map>
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::map<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename Key, typename T, typename Compare, typename Allocator>
void serialize(S &s, std::multimap<Key, T, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_MAP_H

View File

@@ -0,0 +1,45 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H
#include "../ext/std_smart_ptr.h"
namespace bitsery {
template<typename S, typename T, typename D>
void serialize(S &s, std::unique_ptr<T, D> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::shared_ptr<T> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
template<typename S, typename T>
void serialize(S &s, std::weak_ptr<T> &obj) {
s.ext(obj, ext::StdSmartPtr{});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_MEMORY_H

View File

@@ -0,0 +1,42 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H
#include "../ext/std_queue.h"
namespace bitsery {
template<typename S, typename T, typename C>
void serialize(S &s, std::queue<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdQueue{maxSize});
}
template<typename S, typename T, typename C, typename Comp>
void serialize(S &s, std::priority_queue<T, C, Comp> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdQueue{maxSize});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_QUEUE_H

View File

@@ -0,0 +1,43 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H
#include <set>
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::set<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
template<typename S, typename Key, typename Compare, typename Allocator>
void serialize(S &s, std::multiset<Key, Compare, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_SET_H

View File

@@ -0,0 +1,36 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H
#include "../ext/std_stack.h"
namespace bitsery {
template<typename S, typename T, typename C>
void serialize(S &s, std::stack<T, C> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdStack{maxSize});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_STACK_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H
#include "../traits/string.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename CharT, typename Traits, typename Allocator>
void serialize(S &s, std::basic_string<CharT, Traits, Allocator> &str) {
brief_syntax::processContainer(s, str);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_STRING_H

View File

@@ -0,0 +1,35 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H
#include "../ext/std_tuple.h"
namespace bitsery {
template<typename S, typename ...Ts>
void serialize(S &s, std::tuple<Ts...> &obj) {
s.ext(obj, ext::StdTuple{});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_TUPLE_H

View File

@@ -0,0 +1,51 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H
#include <unordered_map>
#include "../ext/std_map.h"
namespace bitsery {
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_map<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
template<typename S, typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdMap{maxSize},
[](S& s, Key& key, T& value) {
s.object(key);
s.object(value);
});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_MAP_H

View File

@@ -0,0 +1,43 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H
#include <unordered_set>
#include "../ext/std_set.h"
namespace bitsery {
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_set<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
template<typename S, typename Key, typename Hash, typename KeyEqual, typename Allocator>
void serialize(S &s, std::unordered_multiset<Key, Hash, KeyEqual, Allocator> &obj, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.ext(obj, ext::StdSet{maxSize});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_UNORDERED_SET_H

View File

@@ -0,0 +1,35 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H
#include "../ext/std_variant.h"
namespace bitsery {
template<typename S, typename ...Ts>
void serialize(S &s, std::variant<Ts...> &obj) {
s.ext(obj, ext::StdVariant{});
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_VARIANT_H

View File

@@ -0,0 +1,37 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
#define BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H
#include "../traits/vector.h"
#include "../details/brief_syntax_common.h"
namespace bitsery {
template<typename S, typename T, typename Allocator>
void serialize(S &s, std::vector<T, Allocator> &obj) {
brief_syntax::processContainer(s, obj);
}
}
#endif //BITSERY_BRIEF_SYNTAX_TYPE_STD_VECTOR_H

52
include/bitsery/common.h Normal file
View File

@@ -0,0 +1,52 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_COMMON_H
#define BITSERY_COMMON_H
#include <tuple>
namespace bitsery {
/*
* endianness
*/
enum class EndiannessType {
LittleEndian,
BigEndian
};
// default configuration for serialization and deserialization
struct DefaultConfig {
// defines endianness of data that is read from input adapter and written to output adapter.
static constexpr EndiannessType Endianness = EndiannessType::LittleEndian;
// these flags allow to improve deserialization performance if data is trusted
// enables/disables checks for buffer end or stream read errors in input adapter
static constexpr bool CheckAdapterErrors = true;
// enables/disables checks for other errors that can significantly affect performance
static constexpr bool CheckDataErrors = true;
};
}
#endif //BITSERY_COMMON_H

View File

@@ -0,0 +1,566 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DESERIALIZER_H
#define BITSERY_DESERIALIZER_H
#include "details/serialization_common.h"
#include "details/adapter_common.h"
#include <utility>
namespace bitsery {
namespace details {
template<typename TAdapter>
class InputAdapterBitPackingWrapper {
public:
static constexpr bool BitPackingEnabled = true;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
InputAdapterBitPackingWrapper(TAdapter& adapter)
: _wrapped{adapter}
{
}
~InputAdapterBitPackingWrapper() {
align();
}
template<size_t SIZE, typename T>
void readBytes(T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
using UT = typename std::make_unsigned<T>::type;
if (!m_scratchBits)
this->_wrapped.template readBytes<SIZE,T>(v);
else
readBits(reinterpret_cast<UT &>(v), details::BitsSize<T>::value);
}
template<size_t SIZE, typename T>
void readBuffer(T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!m_scratchBits) {
this->_wrapped.template readBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
readBits(reinterpret_cast<UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void readBits(T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
readBitsInternal(v, bitsCount);
}
void align() {
if (m_scratchBits) {
ScratchType tmp{};
readBitsInternal(tmp, m_scratchBits);
handleAlignErrors(tmp, std::integral_constant<bool, TConfig::CheckDataErrors>{});
}
}
void currentReadPos(size_t pos) {
align();
this->_wrapped.currentReadPos(pos);
}
size_t currentReadPos() const {
return this->_wrapped.currentReadPos();
}
void currentReadEndPos(size_t pos) {
this->_wrapped.currentReadEndPos(pos);
}
size_t currentReadEndPos() const {
return this->_wrapped.currentReadEndPos();
}
bool isCompletedSuccessfully() const {
return this->_wrapped.isCompletedSuccessfully();
}
ReaderError error() const {
return this->_wrapped.error();
}
void error(ReaderError error) {
this->_wrapped.error(error);
}
private:
TAdapter& _wrapped;
using UnsignedValue = typename std::make_unsigned<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedValue>::type;
ScratchType m_scratch{};
size_t m_scratchBits{};
template<typename T>
void readBitsInternal(T &v, size_t size) {
auto bitsLeft = size;
using TFast = typename FastType<T>::type;
TFast res{};
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, details::BitsSize<UnsignedValue>::value);
if (m_scratchBits < bits) {
UnsignedValue tmp;
this->_wrapped.template readBytes<sizeof(UnsignedValue), UnsignedValue>(tmp);
m_scratch |= static_cast<ScratchType>(tmp) << m_scratchBits;
m_scratchBits += details::BitsSize<UnsignedValue>::value;
}
auto shiftedRes =
static_cast<T>(m_scratch & ((static_cast<ScratchType>(1) << bits) - 1)) << (size - bitsLeft);
res = static_cast<TFast>(res | static_cast<TFast>(shiftedRes));
m_scratch >>= bits;
m_scratchBits -= bits;
bitsLeft -= bits;
}
v = static_cast<T>(res);
}
void handleAlignErrors(ScratchType value, std::true_type) {
if (value)
error(ReaderError::InvalidData);
}
void handleAlignErrors(ScratchType, std::false_type) {
}
};
}
template<typename TInputAdapter, typename TContext = void>
class Deserializer: public details::AdapterAndContextRef<TInputAdapter, TContext> {
public:
//helper type, that always returns bit-packing enabled type, useful inside deserialize function when enabling bitpacking
using BPEnabledType = Deserializer<typename std::conditional<TInputAdapter::BitPackingEnabled,
TInputAdapter,
details::InputAdapterBitPackingWrapper<TInputAdapter>>::type, TContext>;
using TConfig = typename TInputAdapter::TConfig;
using details::AdapterAndContextRef<TInputAdapter, TContext>::AdapterAndContextRef;
/*
* object function
*/
template<typename T>
void object(T &&obj) {
details::SerializeFunction<Deserializer, T>::invoke(*this, std::forward<T>(obj));
}
template<typename T, typename Fnc>
void object(T &&obj, Fnc &&fnc) {
fnc(*this, std::forward<T>(obj));
}
/*
* functionality, that enables simpler serialization syntax, by including additional header
*/
template <typename... TArgs>
Deserializer &operator()(TArgs &&... args) {
archive(std::forward<TArgs>(args)...);
return *this;
}
/*
* value
*/
template<size_t VSIZE, typename T>
void value(T &v) {
static_assert(details::IsFundamentalType<T>::value, "Value must be integral, float or enum type.");
using TValue = typename details::IntegralFromFundamental<T>::TValue;
this->_adapter.template readBytes<VSIZE>(reinterpret_cast<TValue &>(v));
}
/*
* enable bit-packing
*/
template <typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc), std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{});
}
/*
* extension functions
*/
template<typename T, typename Ext, typename Fnc>
void ext(T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.deserialize(*this, obj, std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, typename Ext>
void ext(T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, obj, [](Deserializer& s, VType &v) { s.value<VSIZE>(v);});
}
template<typename T, typename Ext>
void ext(T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, obj, [](Deserializer& s, VType &v) { s.object(v); });
}
/*
* boolValue
*/
void boolValue(bool &v) {
procBoolValue(v,
std::integral_constant<bool, TInputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
}
/*
* text overloads
*/
template<size_t VSIZE, typename T>
void text(T &str, size_t maxSize) {
static_assert(details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use text(T&) overload without `maxSize` for static containers");
size_t length;
readSize(length, maxSize);
traits::ContainerTraits<T>::resize(str, length + (traits::TextTraits<T>::addNUL ? 1u : 0u));
procText<VSIZE>(str, length);
}
template<size_t VSIZE, typename T>
void text(T &str) {
static_assert(details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
size_t length;
readSize(length, traits::ContainerTraits<T>::size(str));
procText<VSIZE>(str, length);
}
/*
* container overloads
*/
//dynamic size containers
template<typename T, typename Fnc>
void container(T &obj, size_t maxSize, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
traits::ContainerTraits<T>::resize(obj, size);
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
traits::ContainerTraits<T>::resize(obj, size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(T&) overload without `maxSize` for static containers");
size_t size{};
readSize(size, maxSize);
traits::ContainerTraits<T>::resize(obj, size);
procContainer(std::begin(obj), std::end(obj));
}
//fixed size containers
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
void container(T &obj, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
procContainer(std::begin(obj), std::end(obj));
}
//overloads for functions with explicit type size
template<typename T>
void value1b(T &&v) { value<1>(std::forward<T>(v)); }
template<typename T>
void value2b(T &&v) { value<2>(std::forward<T>(v)); }
template<typename T>
void value4b(T &&v) { value<4>(std::forward<T>(v)); }
template<typename T>
void value8b(T &&v) { value<8>(std::forward<T>(v)); }
template<typename T, typename Ext>
void ext1b(T &v, Ext &&extension) { ext<1, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext2b(T &v, Ext &&extension) { ext<2, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext4b(T &v, Ext &&extension) { ext<4, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext8b(T &v, Ext &&extension) { ext<8, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T>
void text1b(T &str, size_t maxSize) { text<1>(str, maxSize); }
template<typename T>
void text2b(T &str, size_t maxSize) { text<2>(str, maxSize); }
template<typename T>
void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T>
void text1b(T &str) { text<1>(str); }
template<typename T>
void text2b(T &str) { text<2>(str); }
template<typename T>
void text4b(T &str) { text<4>(str); }
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
template<typename T>
void container2b(T &&obj, size_t maxSize) { container<2>(std::forward<T>(obj), maxSize); }
template<typename T>
void container4b(T &&obj, size_t maxSize) { container<4>(std::forward<T>(obj), maxSize); }
template<typename T>
void container8b(T &&obj, size_t maxSize) { container<8>(std::forward<T>(obj), maxSize); }
template<typename T>
void container1b(T &&obj) { container<1>(std::forward<T>(obj)); }
template<typename T>
void container2b(T &&obj) { container<2>(std::forward<T>(obj)); }
template<typename T>
void container4b(T &&obj) { container<4>(std::forward<T>(obj)); }
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
private:
void readSize(size_t& size, size_t maxSize) {
details::readSize(this->_adapter, size, maxSize,
std::integral_constant<bool, TInputAdapter::TConfig::CheckDataErrors>{});
}
//process value types
//false_type means that we must process all elements individually
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::false_type) {
for (; first != last; ++first)
value<VSIZE>(*first);
}
//process value types
//true_type means, that we can copy whole buffer
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::true_type) {
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last){
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
template<typename It, typename Fnc>
void procContainer(It first, It last, Fnc fnc) {
for (; first != last; ++first)
fnc(*this, *first);
}
//process object types
template<typename It>
void procContainer(It first, It last) {
for (; first != last; ++first)
object(*first);
}
template <size_t VSIZE, typename T>
void procText(T& str, size_t length) {
auto begin = std::begin(str);
//end of string, not end of container
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)
*end = {};
}
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
template <typename HandleDataErrors>
void procBoolValue(bool &v, std::true_type, HandleDataErrors) {
uint8_t tmp{};
this->_adapter.readBits(tmp, 1);
v = tmp == 1;
}
void procBoolValue(bool &v, std::false_type, std::true_type) {
uint8_t tmp{};
this->_adapter.template readBytes<1>(tmp);
if (tmp > 1)
this->_adapter.error(ReaderError::InvalidData);
v = tmp == 1;
}
void procBoolValue(bool &v, std::false_type, std::false_type) {
uint8_t tmp{};
this->_adapter.template readBytes<1>(tmp);
v = tmp > 0;
}
//enable bit-packing or do nothing if it is already enabled
template <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::true_type) {
fnc(*this);
}
template <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type) {
//create deserializer using bitpacking wrapper
auto des = createWithContext(std::integral_constant<bool, Deserializer::HasContext>{});
fnc(des);
}
BPEnabledType createWithContext(std::true_type) {
return BPEnabledType{this->_context, this->_adapter};
}
BPEnabledType createWithContext(std::false_type) {
return BPEnabledType{this->_adapter};
}
//these are dummy functions for extensions that have TValue = void
void object(details::DummyType&) {
}
template <size_t VSIZE>
void value(details::DummyType&) {
}
template<typename T, typename ... TArgs>
void archive(T &&head, TArgs &&... tail) {
//serialize object
details::BriefSyntaxFunction<Deserializer, T>::invoke(*this, std::forward<T>(head));
//expand other elements
archive(std::forward<TArgs>(tail)...);
}
//dummy function, that stops archive variadic arguments expansion
void archive() {
}
};
//helper type
//helper function that set ups all the basic steps and after deserialziation returns status
template <typename InputAdapter, typename T>
std::pair<ReaderError, bool> quickDeserialization(InputAdapter adapter, T& value) {
Deserializer<InputAdapter> des{std::move(adapter)};
des.object(value);
return {des.adapter().error(), des.adapter().isCompletedSuccessfully()};
}
template <typename Context , typename InputAdapter, typename T>
std::pair<ReaderError, bool> quickDeserialization(Context& ctx, InputAdapter adapter, T& value) {
Deserializer<InputAdapter, Context> des{ctx, std::move(adapter)};
des.object(value);
return {des.adapter().error(), des.adapter().isCompletedSuccessfully()};
}
}
#endif //BITSERY_DESERIALIZER_H

View File

@@ -0,0 +1,367 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_ADAPTER_COMMON_H
#define BITSERY_DETAILS_ADAPTER_COMMON_H
#include <algorithm>
#include <utility>
#include <cassert>
#include <vector>
#include <stack>
#include <cstring>
#include <climits>
#include "not_defined_type.h"
#include "../common.h"
namespace bitsery {
enum class ReaderError {
NoError,
ReadingError, // this might be used with stream adapter
DataOverflow,
InvalidData,
InvalidPointer
};
namespace details {
/**
* size read/write functions
*/
template <typename Reader, typename TCheckMaxSize>
void readSize(Reader& r, size_t& size, size_t maxSize, TCheckMaxSize) {
uint8_t hb{};
r.template readBytes<1>(hb);
if (hb < 0x80u) {
size = hb;
} else {
uint8_t lb{};
r.template readBytes<1>(lb);
if (hb & 0x40u) {
uint16_t lw{};
r.template readBytes<2>(lw);
size = ((((hb & 0x3Fu) << 8) | lb) << 16) | lw;
} else {
size = ((hb & 0x7Fu) << 8) | lb;
}
}
handleReadMaxSize(r, size, maxSize, TCheckMaxSize{});
}
template <typename Reader>
void handleReadMaxSize(Reader& r, size_t& size, size_t maxSize, std::true_type) {
if (size > maxSize) {
r.error(ReaderError::InvalidData);
size = {};
}
}
template <typename Reader>
void handleReadMaxSize(Reader&, size_t&, size_t, std::false_type) {
}
template <typename Writer>
void writeSize(Writer& w, const size_t size) {
if (size < 0x80u) {
w.template writeBytes<1>(static_cast<uint8_t>(size));
} else {
if (size < 0x4000u) {
w.template writeBytes<1>(static_cast<uint8_t>((size >> 8) | 0x80u));
w.template writeBytes<1>(static_cast<uint8_t>(size));
} else {
assert(size < 0x40000000u);
w.template writeBytes<1>(static_cast<uint8_t>((size >> 24) | 0xC0u));
w.template writeBytes<1>(static_cast<uint8_t>(size >> 16));
w.template writeBytes<2>(static_cast<uint16_t>(size));
}
}
}
/**
* swap utils
*/
//add swap functions to class, to avoid compilation warning about unused functions
struct SwapImpl {
static uint64_t exec(uint64_t value) {
#ifdef __GNUC__
return __builtin_bswap64(value);
#else
value = ( value & 0x00000000FFFFFFFF ) << 32 | ( value & 0xFFFFFFFF00000000 ) >> 32;
value = ( value & 0x0000FFFF0000FFFF ) << 16 | ( value & 0xFFFF0000FFFF0000 ) >> 16;
value = ( value & 0x00FF00FF00FF00FF ) << 8 | ( value & 0xFF00FF00FF00FF00 ) >> 8;
return value;
#endif
}
static uint32_t exec(uint32_t value) {
#ifdef __GNUC__
return __builtin_bswap32(value);
#else
return ( value & 0x000000ff ) << 24 | ( value & 0x0000ff00 ) << 8 | ( value & 0x00ff0000 ) >> 8 | ( value & 0xff000000 ) >> 24;
#endif
}
static uint16_t exec(uint16_t value) {
return static_cast<uint16_t>((value & 0x00ff) << 8 | (value & 0xff00) >> 8);
}
static uint8_t exec(uint8_t value) {
return value;
}
};
template<typename TValue>
TValue swap(TValue value) {
constexpr size_t TSize = sizeof(TValue);
using UT = typename std::conditional<TSize == 1, uint8_t,
typename std::conditional<TSize == 2, uint16_t,
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::type;
return 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
// 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,
"system must be either little or big endian");
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian
: EndiannessType::BigEndian;
}
template <typename Config>
using ShouldSwap = std::integral_constant<bool, Config::Endianness != details::getSystemEndianness()>;
/**
* helper types to work with bits
*/
template<typename T>
struct BitsSize:public std::integral_constant<size_t, sizeof(T) * 8> {
static_assert(CHAR_BIT == 8, "only support systems with byte size of 8 bits");
};
template<typename T>
struct ScratchType {
using type = NotDefinedType;
};
template<>
struct ScratchType<uint8_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
*/
template<typename Adapter>
struct OutputAdapterBaseCRTP {
static constexpr bool BitPackingEnabled = false;
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
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, "");
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{});
}
template<typename T>
void writeBits(const T &, size_t ) {
static_assert(std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Serializer with bit packing enabled.");
}
void align() {
}
OutputAdapterBaseCRTP() = default;
OutputAdapterBaseCRTP(const OutputAdapterBaseCRTP&) = delete;
OutputAdapterBaseCRTP& operator = (const OutputAdapterBaseCRTP&) = delete;
OutputAdapterBaseCRTP(OutputAdapterBaseCRTP&&) = default;
OutputAdapterBaseCRTP& operator = (OutputAdapterBaseCRTP&&) = default;
private:
template<typename T>
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)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
});
}
template<typename 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));
}
};
template <typename Base>
struct InputAdapterBaseCRTP {
static constexpr bool BitPackingEnabled = false;
template<size_t SIZE, typename T>
void readBytes(T& v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
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, "");
static_cast<Base*>(this)->readInternalBuffer(reinterpret_cast<typename Base::TValue *>(buf), sizeof(T) * count);
swapDataBits(buf, count, ShouldSwap<typename Base::TConfig>{});
}
template<typename T>
void readBits(T&, size_t) {
static_assert(std::is_void<T>::value,
"Bit-packing is not enabled.\nEnable by call to `enableBitPacking`) or create Deserializer with bit packing enabled.");
}
void align() {
}
InputAdapterBaseCRTP() = default;
InputAdapterBaseCRTP(const InputAdapterBaseCRTP&) = delete;
InputAdapterBaseCRTP& operator = (const InputAdapterBaseCRTP&) = delete;
InputAdapterBaseCRTP(InputAdapterBaseCRTP&&) = default;
InputAdapterBaseCRTP& operator = (InputAdapterBaseCRTP&&) = default;
virtual ~InputAdapterBaseCRTP() = default;
private:
template<typename T>
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 *, 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
}
};
}
}
#endif //BITSERY_DETAILS_ADAPTER_COMMON_H

View File

@@ -0,0 +1,149 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H
#define BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H
#include "../traits/core/traits.h"
#include <limits>
namespace bitsery {
namespace brief_syntax {
//these function overloads is required to apply maxSize, and optimize for fundamental types
//for contigous arrays of fundamenal types, memcpy will be applied
template<typename S, typename T, typename std::enable_if<
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template container<sizeof(TValue)>(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
s.container(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& !traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c) {
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template container<sizeof(TValue)>(c);
}
template<typename S, typename T, typename std::enable_if<
!details::IsFundamentalType<typename traits::ContainerTraits<T>::TValue>::value
&& !traits::ContainerTraits<T>::isResizable
>::type * = nullptr>
void processContainer(S &s, T &c) {
s.container(c);
}
//overloads for text processing to apply maxSize
template<typename S, typename T, typename std::enable_if<
traits::ContainerTraits<T>::isResizable>::type * = nullptr>
void processText(S &s, T &c, size_t maxSize = std::numeric_limits<size_t>::max()) {
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template text<sizeof(TValue)>(c, maxSize);
}
template<typename S, typename T, typename std::enable_if<
!traits::ContainerTraits<T>::isResizable>::type * = nullptr>
void processText(S &s, T &c) {
using TValue = typename traits::ContainerTraits<T>::TValue;
s.template text<sizeof(TValue)>(c);
}
//all wrapper functions, that modify behaviour, should inherit from this
struct ModFnc {
};
//this type is used to differentiate between container and text behaviour
template<typename T, size_t N, bool isText>
struct CArray : public ModFnc {
CArray(T (&data_)[N]) : data{data_} {};
T (&data)[N];
};
template<typename S, typename T, size_t N>
void serialize(S &s, CArray<T, N, true> &str) {
processText(s, str.data);
}
template<typename S, typename T, size_t N>
void serialize(S &s, CArray<T, N, false> &obj) {
processContainer(s, obj.data);
}
//used to set max container size
template<typename T>
struct MaxSize : public ModFnc {
MaxSize(T &data_, size_t maxSize_) : data{data_}, maxSize{maxSize_} {};
T &data;
size_t maxSize;
};
//if container, then call procesContainer, this memcpy for fundamental types contiguous container
template<typename S, typename T>
void processMaxSize(S &s, T& data, size_t maxSize, std::true_type) {
processContainer(s, data, maxSize);
}
//overload for const T&
template<typename S, typename T>
void processMaxSize(S &s, const T& data, size_t maxSize, std::true_type) {
processContainer(s, const_cast<T&>(data), maxSize);
}
//try to call serialize overload with maxsize, extensions use this technique
template<typename S, typename T>
void processMaxSize(S &s, T& data, size_t maxSize, std::false_type) {
serialize(s, data, maxSize);
}
//overload for const T&
template<typename S, typename T>
void processMaxSize(S &s, const T& data, size_t maxSize, std::false_type) {
serialize(s, const_cast<T&>(data), maxSize);
}
template<typename S, typename T>
void serialize(S &s, const MaxSize<T> &ms) {
processMaxSize(s, ms.data, ms.maxSize, details::IsContainerTraitsDefined<typename std::decay<T>::type>{});
}
}
}
#endif //BITSERY_DETAILS_BRIEF_SYNTAX_COMMON_H

View File

@@ -0,0 +1,79 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_NOT_DEFINED_TYPE_H
#define BITSERY_DETAILS_NOT_DEFINED_TYPE_H
#include <iterator>
namespace bitsery {
namespace details {
//this type is used to show clearer error messages
struct NotDefinedType {
//just swallow anything that is passed during creating
template <typename ... T>
NotDefinedType(T&& ...){}
NotDefinedType() = default;
//define operators so that we also swallow deeper errors, to reduce error stack
//this time will be used as iterator, so define all operators necessary to work with iterators
friend bool operator == (const NotDefinedType&, const NotDefinedType&) {
return true;
}
friend bool operator != (const NotDefinedType&, const NotDefinedType&) {
return false;
}
NotDefinedType& operator += (int) {
return *this;
}
NotDefinedType& operator -= (int) {
return *this;
}
friend int operator - (const NotDefinedType&, const NotDefinedType&) {
return 0;
}
int& operator*() {
return data;
}
int data{};
};
template <typename T>
struct IsDefined:public std::integral_constant<bool, !std::is_same<NotDefinedType, T>::value> {
};
}
}
namespace std {
//define iterator traits to work with standart algorithms
template <>
struct iterator_traits<bitsery::details::NotDefinedType> {
using difference_type = int;
using value_type = int;
using pointer = int*;
using reference = int&;
using iterator_category = std::random_access_iterator_tag;
};
}
#endif //BITSERY_DETAILS_NOT_DEFINED_TYPE_H

View File

@@ -0,0 +1,465 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_DETAILS_SERIALIZATION_COMMON_H
#define BITSERY_DETAILS_SERIALIZATION_COMMON_H
#include <type_traits>
#include <utility>
#include <tuple>
#include "adapter_common.h"
#include "../traits/core/traits.h"
namespace bitsery {
//this allows to call private serialize method, and construct instance (if no default constructor is provided) for your type
//just make friend it in your class
class Access {
public:
template<typename S, typename T>
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
obj.serialize(s);
}
template <typename T>
static T create() {
//if you get an error here, please create default constructor
return T{};
}
template <typename T>
static T* create(void* ptr) {
return new(ptr) T{};
}
};
// convenient functors that can be passed as lambda to serializer/deserializer instead of writing lambda
// e.g. instead of writing this:
// s.container(c, 100, [](S& s, float& v) { s.ext4b(v, CompactValue{});});
// you can write like this
// s.container(c, 100, FtorExtValue2b<CompactValue>{});
template<size_t N, typename Ext>
struct FtorExtValue : public Ext {
template <typename S, typename T>
void operator()(S& s, T& v) const {
s.template ext<N>(v, static_cast<const Ext&>(*this));
}
};
template <typename Ext>
struct FtorExtValue1b: FtorExtValue<1, Ext> {};
template <typename Ext>
struct FtorExtValue2b: FtorExtValue<2, Ext> {};
template <typename Ext>
struct FtorExtValue4b: FtorExtValue<4, Ext> {};
template <typename Ext>
struct FtorExtValue8b: FtorExtValue<8, Ext> {};
template<typename Ext>
struct FtorExtObject : public Ext {
template <typename S, typename T>
void operator()(S& s, T& v) const {
s.ext(v, static_cast<const Ext&>(*this));
}
};
//when call to serialize function is ambiguous (member and non-member serialize function exists for a type)
//specialize this class by inheriting from either UseNonMemberFnc or UseMemberFnc
//e.g.
//template <> struct SelectSerializeFnc<MyDerivedClass>:UseMemberFnc {};
template<typename T>
struct SelectSerializeFnc : std::integral_constant<int, 0> {
};
//types you need to inherit from when specializing SelectSerializeFnc class
struct UseNonMemberFnc : std::integral_constant<int, 1> {
};
struct UseMemberFnc : std::integral_constant<int, 2> {
};
namespace details {
//helper types for error handling
template<typename T>
struct IsContainerTraitsDefined : public IsDefined<typename traits::ContainerTraits<T>::TValue> {
};
template<typename T>
struct IsTextTraitsDefined : public IsDefined<typename traits::TextTraits<T>::TValue> {
};
template<typename Ext, typename T>
struct IsExtensionTraitsDefined : public IsDefined<typename traits::ExtensionTraits<Ext, T>::TValue> {
};
#ifdef _MSC_VER
//helper types for HasSerializeFunction
template <typename S, typename T>
using TrySerializeFunction = decltype(serialize(std::declval<S &>(), std::declval<T &>()));
template <typename S, typename T>
struct HasSerializeFunctionHelper {
template <typename Q, typename R, typename = TrySerializeFunction<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
using type = decltype(tester(std::declval<S>(), std::declval<T>()));
};
template <typename S, typename T>
struct HasSerializeFunction :HasSerializeFunctionHelper<S, T>::type {};
//helper types for HasSerializeMethod
template <typename S, typename T>
using TrySerializeMethod = decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()));
template <typename S, typename T>
struct HasSerializeMethodHelper {
template <typename Q, typename R, typename = TrySerializeMethod<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
using type = decltype(tester(std::declval<S>(), std::declval<T>()));
};
template <typename S, typename T>
struct HasSerializeMethod :HasSerializeMethodHelper<S, T>::type {};
//helper types for IsBriefSyntaxIncluded
template <typename S, typename T>
using TryProcessBriefSyntax = decltype(processBriefSyntax(std::declval<S &>(), std::declval<T &&>()));
template <typename S, typename T>
struct IsBriefSyntaxIncludedHelper {
template <typename Q, typename R, typename = TryProcessBriefSyntax<Q, R>>
static std::true_type tester(Q&&, R&&);
static std::false_type tester(...);
using type = decltype(tester(std::declval<S>(), std::declval<T>()));
};
template <typename S, typename T>
struct IsBriefSyntaxIncluded :IsBriefSyntaxIncludedHelper<S, T>::type {};
#else
//helper metafunction, that is added to c++17
template<typename... Ts>
struct make_void {
typedef void type;
};
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
template<typename, typename, typename = void>
struct HasSerializeFunction : std::false_type {
};
template<typename S, typename T>
struct HasSerializeFunction<S, T,
void_t<decltype(serialize(std::declval<S &>(), std::declval<T &>()))>
> : std::true_type {
};
template<typename, typename, typename = void>
struct HasSerializeMethod : std::false_type {
};
template<typename S, typename T>
struct HasSerializeMethod<S, T,
void_t<decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()))>
> : std::true_type {
};
//this solution doesn't work with visual studio, but is more elegant
template<typename, typename, typename = void>
struct IsBriefSyntaxIncluded : std::false_type {
};
template<typename S, typename T>
struct IsBriefSyntaxIncluded<S, T,
void_t<decltype(processBriefSyntax(std::declval<S &>(), std::declval<T &&>()))>
> : std::true_type {
};
#endif
//used for extensions when extension TValue = void
struct DummyType {
};
/*
* this includes all integral types, floats and enums(except bool)
*/
template<typename T>
struct IsFundamentalType : std::integral_constant<bool,
std::is_enum<T>::value
|| std::is_floating_point<T>::value
|| std::is_integral<T>::value> {
};
template<typename T, typename Integral = void>
struct IntegralFromFundamental {
using TValue = T;
};
template<typename T>
struct IntegralFromFundamental<T, typename std::enable_if<std::is_enum<T>::value>::type> {
using TValue = typename std::underlying_type<T>::type;
};
template<typename T>
struct IntegralFromFundamental<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
using TValue = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
};
template<typename T>
struct UnsignedFromFundamental {
using type = typename std::make_unsigned<typename IntegralFromFundamental<T>::TValue>::type;
};
template<typename T>
using SameSizeUnsigned = typename UnsignedFromFundamental<T>::type;
/*
* functions for object serialization
*/
template<typename S, typename T>
struct SerializeFunction {
static void invoke(S &s, T &v) {
static_assert(HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value,
"\nPlease define 'serialize' function for your type (inside or outside of class):\n"
" template<typename S>\n"
" void serialize(S& s)\n"
" {\n"
" ...\n"
" }\n");
using TDecayed = typename std::decay<T>::type;
selectSerializeFnc(s, v, SelectSerializeFnc<TDecayed>{});
}
static constexpr bool isDefined() {
return HasSerializeFunction<S, T>::value || HasSerializeMethod<S, T>::value;
}
private:
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 0>) {
static_assert(!(HasSerializeFunction<S, T>::value && HasSerializeMethod<S, T>::value),
"\nPlease define only one 'serialize' function (member OR free).\n"
"If serialization function is inherited from base class, then explicitly select correct function for your type e.g.:\n"
" template <>\n"
" struct SelectSerializeFnc<DerivedClass>:UseMemberFnc {};\n");
selectSerializeFnc(s, v, std::integral_constant<int,
HasSerializeFunction<S, T>::value ? 1 : 2>{});
}
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 1>) {
serialize(s, v);
}
static void selectSerializeFnc(S &s, T &v, std::integral_constant<int, 2>) {
Access::serialize(s, v);
}
};
/*
* functions for object serialization
*/
template<typename S, typename T, typename Enabled = void>
struct BriefSyntaxFunction {
static void invoke(S &s, T &&obj) {
static_assert(IsBriefSyntaxIncluded<S, T>::value,
"\nPlease include '<bitsery/brief_syntax.h>' to use operator():\n");
processBriefSyntax(s, std::forward<T>(obj));
}
};
/*
* helper function for getting context from serializer/deserializer
*/
template<int Index, typename... Conds>
struct FindIndex : std::integral_constant<int, Index> {};
template<int Index, typename Cond, typename... Conds>
struct FindIndex<Index, Cond, Conds...> :
std::conditional<Cond::value, std::integral_constant<int, Index>, FindIndex<Index+1, Conds...>>::type
{};
template <typename T, typename Tuple>
struct GetConvertibleTypeIndexFromTuple;
template <typename T, typename... Us>
struct GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>> : FindIndex<0, std::is_convertible<Us&, T&>...> {};
template <typename T, typename Tuple>
struct IsExistsConvertibleTupleType;
template <typename T, typename... Us>
struct IsExistsConvertibleTupleType<T, std::tuple<Us...>> :
std::integral_constant<bool, GetConvertibleTypeIndexFromTuple<T, std::tuple<Us...>>::value != sizeof...(Us)> {};
/*
* get context from internal or external, and check if it's convertible or not
*/
template<bool AssertExists, typename TCast, typename TContext>
TCast* getDirectlyIfExists(TContext& ctx, std::true_type) {
return &static_cast<TCast&>(ctx);
}
template<bool AssertExists, typename TCast, typename TContext>
TCast* getDirectlyIfExists(TContext& , std::false_type) {
// TCast cannot be convertible from provided context
static_assert(!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
return nullptr;
}
template<bool AssertExists, typename TCast, typename ... TArgs>
TCast* getFromTupleIfExists(std::tuple<TArgs...>& ctx, std::true_type) {
using TupleIndex = GetConvertibleTypeIndexFromTuple<TCast, std::tuple<TArgs...>>;
return &static_cast<TCast&>(std::get<TupleIndex::value>(ctx));
}
template<bool AssertExists, typename TCast, typename ... TArgs>
TCast* getFromTupleIfExists(std::tuple<TArgs...>& , std::false_type) {
// TCast cannot be convertible from provided context
static_assert(!AssertExists,
"Invalid context cast. Context type doesn't exists.\nSome functionality requires (de)seserializer to have specific context.");
return nullptr;
}
//non tuple context
template<bool AssertExists, typename TCast, typename TContext>
TCast* getContext(TContext& ctx) {
return getDirectlyIfExists<AssertExists, TCast>(ctx, std::is_convertible<TContext&, TCast&>{});
}
//tuple context
template<bool AssertExists, typename TCast, typename ... TArgs>
TCast* getContext(std::tuple<TArgs...>& ctx) {
return getFromTupleIfExists<AssertExists, TCast>(ctx, IsExistsConvertibleTupleType<TCast, std::tuple<TArgs...>>{});
}
template <typename Adapter, typename Context>
class AdapterAndContextRef {
public:
static constexpr bool HasContext = true;
using Config = typename Adapter::TConfig;
// constructing adapter in place is important,
// because enableBitPacking might create instance with bit write/read enabled adapter wrapper,
// which has non trivial destructor
template <typename ... TArgs>
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
: _adapter{std::forward<TArgs>(args)...},
_context{ctx}
{
}
/*
* get serialization context.
* this is optional, but might be required for some specific serialization flows.
*/
template <typename T>
T& context() {
return *getContext<true, T>(_context);
}
template <typename T>
T* contextOrNull() {
return getContext<false, T>(_context);
}
Adapter& adapter() & {
return _adapter;
}
Adapter adapter() && {
return std::move(_adapter);
}
protected:
Adapter _adapter;
Context& _context;
};
template <typename Adapter>
class AdapterAndContextRef<Adapter, void> {
public:
static constexpr bool HasContext = false;
using Config = typename Adapter::TConfig;
template <typename ... TArgs>
explicit AdapterAndContextRef(TArgs&& ... args)
: _adapter{std::forward<TArgs>(args)...}
{
}
template <typename T>
T& context() {
static_assert(std::is_void<T>::value, "Context is not defined (is void).");
}
template <typename T>
T* contextOrNull() {
return nullptr;
}
Adapter& adapter() & {
return _adapter;
}
Adapter adapter() && {
return std::move(_adapter);
}
protected:
Adapter _adapter;
};
/**
* other helper meta-functions
*/
template<typename T, template<typename...> class Template>
struct IsSpecializationOf : std::false_type {
};
template<template<typename...> class Template, typename... Args>
struct IsSpecializationOf<Template<Args...>, Template> : std::true_type {
};
}
}
#endif //BITSERY_DETAILS_SERIALIZATION_COMMON_H

View File

@@ -0,0 +1,190 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_COMPACT_VALUE_H
#define BITSERY_EXT_COMPACT_VALUE_H
#include "../details/serialization_common.h"
#include "../details/adapter_common.h"
#include <cassert>
namespace bitsery {
namespace details {
template <bool CheckOverflow>
class CompactValueImpl {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &s, const T &v, Fnc &&) const {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
serializeImpl(s.adapter(), reinterpret_cast<const TValue&>(v), std::integral_constant<bool, sizeof(T) != 1>{});
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &d, T &v, Fnc &&) const {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
using TValue = typename IntegralFromFundamental<T>::TValue;
deserializeImpl(d.adapter(), reinterpret_cast<TValue &>(v), std::integral_constant<bool, sizeof(T) != 1>{});
}
private:
// if value is 1byte size, just serialize/ deserialize whole value
template<typename Writer, typename T>
void serializeImpl(Writer &writer, const T &v, std::false_type) const {
writer.template writeBytes<1>(v);
}
template<typename Reader, typename T>
void deserializeImpl(Reader &reader, T &v, std::false_type) const {
reader.template readBytes<1>(v);
}
// when value is bigger than 1byte size,
template<typename Writer, typename T>
void serializeImpl(Writer &writer, const T &v, std::true_type) const {
auto val = zigZagEncode(v, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
writeBytes(writer, val);
}
template<typename Reader, typename T>
void deserializeImpl(Reader &reader, T &v, std::true_type) const {
using TUnsigned = SameSizeUnsigned<T>;
TUnsigned res{};
readBytes<Reader::TConfig::CheckDataErrors>(reader, res);
v = zigZagDecode<T>(res, std::is_signed<typename IntegralFromFundamental<T>::TValue>{});
}
// zigzag encode signed types
template<typename T>
const SameSizeUnsigned<T> &zigZagEncode(const T &v, std::false_type) const {
return v;
}
template<typename TResult, typename TUnsigned>
const TResult &zigZagDecode(const TUnsigned &v, std::false_type) const{
return v;
}
template<typename T>
SameSizeUnsigned<T> zigZagEncode(const T &v, std::true_type) const {
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 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 {
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;
}
w.template writeBytes<1>(static_cast<uint8_t>(val));
}
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);
tmp += static_cast<TFast>(b1 & 0x7Fu) << i;
}
v = static_cast<T>(tmp);
handleReadOverflow<Reader, T>(r, i, b1,
std::integral_constant<bool, CheckOverflow && CheckErrors>{});
}
template <typename Reader, typename T>
void handleReadOverflow(Reader& r, unsigned shiftedBy, uint8_t remainder, std::true_type) const {
constexpr auto TBITS = sizeof(T)*8;
if (shiftedBy > TBITS && remainder >> (TBITS + 7 - shiftedBy)) {
r.error(bitsery::ReaderError::InvalidData);
}
}
template <typename Reader, typename T>
void handleReadOverflow(Reader &, unsigned , uint8_t , std::false_type) const {
}
};
}
namespace ext {
// this type will use value overload, and do not check if type is sufficiently large during deserialization
class CompactValue: public details::CompactValueImpl<false> {};
// this type will enable object overload, and set DataOverflow if value doesn't fit in type, during deserialization
class CompactValueAsObject: public details::CompactValueImpl<true> {};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::CompactValue, T> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
// disable object overload, because we don't have implemented serialization function for fundamental types
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::CompactValueAsObject, T> {
// use dummy implemenations for value and object overload
using TValue = void;
// only enable object overload
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T, bool Check>
struct ExtensionTraits<details::CompactValueImpl<Check>, T> {
using TValue = T;
static constexpr bool SupportValueOverload = !Check;
static constexpr bool SupportObjectOverload = Check;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_COMPACT_VALUE_H

View File

@@ -0,0 +1,104 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_ENTROPY_H
#define BITSERY_EXT_ENTROPY_H
#include "value_range.h"
namespace bitsery {
namespace details {
template<typename TValue, typename TContainer>
size_t findEntropyIndex(const TValue &v, const TContainer &defValues) {
size_t index{1u};
for (auto &d:defValues) {
if (d == v)
return index;
++index;
}
return 0u;
}
}
namespace ext {
template<typename TContainer>
class Entropy {
public:
/**
* Allows entropy-encoding technique, by writing few bits for most common values
* @param values list of most common values
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing bits for index
*/
constexpr Entropy(TContainer& values, bool alignBeforeData=true)
: _values{values},
_alignBeforeData{alignBeforeData} {
};
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &s, const T &obj, Fnc &&fnc) const {
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
auto index = details::findEntropyIndex(obj, _values);
s.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
if (_alignBeforeData)
s.adapter().align();
if (!index)
fnc(s, const_cast<T &>(obj));
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &d, T &obj, Fnc &&fnc) const {
assert(traits::ContainerTraits<TContainer>::size(_values) > 0);
size_t index{};
d.ext(index, ext::ValueRange<size_t>{0u, traits::ContainerTraits<TContainer>::size(_values)});
if (_alignBeforeData)
d.adapter().align();
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);
}
private:
TContainer& _values;
bool _alignBeforeData;
};
}
namespace traits {
template<typename TContainer, typename T>
struct ExtensionTraits<ext::Entropy<TContainer>, T> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_ENTROPY_H

View File

@@ -0,0 +1,84 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_GROWABLE_H
#define BITSERY_EXT_GROWABLE_H
#include "../traits/core/traits.h"
namespace bitsery {
namespace ext {
/*
* enables forward and backward compatibility, by allowing to append additional data at the end of serialization
* old deserialization method will ignore additional data by jumping through it at the end of deserialization flow
* new deserialization method will read all 0 for new fields if there is no data for it
*/
class Growable {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
auto& writer = ser.adapter();
const auto startPos = writer.currentWritePos();
writer.template writeBytes<4>(static_cast<uint32_t>(0));
fnc(ser, const_cast<T&>(obj));
const auto endPos = writer.currentWritePos();
writer.currentWritePos(startPos);
writer.template writeBytes<4>(static_cast<uint32_t>(endPos - startPos));
writer.currentWritePos(endPos);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
auto& reader = des.adapter();
uint32_t size{};
const auto readEndPos = reader.currentReadEndPos();
const auto startPos = reader.currentReadPos();
reader.template readBytes<4>(size);
reader.currentReadEndPos(startPos + size);
fnc(des, obj);
reader.currentReadPos(startPos + size);
reader.currentReadEndPos(readEndPos);
}
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::Growable, T> {
using TValue = T;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_GROWABLE_H

View File

@@ -0,0 +1,162 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_INHERITANCE_H
#define BITSERY_EXT_INHERITANCE_H
#include <unordered_set>
#include "../traits/core/traits.h"
#include "../ext/utils/memory_resource.h"
namespace bitsery {
namespace ext {
//required when virtual inheritance (ext::VirtualBaseClass) exists in serialization flow.
//for standard inheritance (ext::BaseClass) it is optional.
class InheritanceContext {
public:
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
:_virtualBases{pointer_utils::StdPolyAlloc<const void*>{memResource}}
{}
InheritanceContext(const InheritanceContext&) = delete;
InheritanceContext&operator = (const InheritanceContext&) = delete;
InheritanceContext(InheritanceContext&&) = default;
InheritanceContext& operator = (InheritanceContext&&) = default;
template <typename TDerived, typename TBase>
void beginBase(const TDerived &derived, const TBase &) {
if (_depth == 0) {
const void* ptr = std::addressof(derived);
if ( _parentPtr != ptr)
_virtualBases.clear();
_parentPtr = ptr;
}
++_depth;
}
template <typename TDerived, typename TBase>
bool beginVirtualBase(const TDerived &derived, const TBase &base) {
beginBase(derived, base);
return _virtualBases.emplace(std::addressof(base)).second;
}
void end() {
--_depth;
}
private:
//these members are required to know when we can clear _virtualBases
size_t _depth{};
const void* _parentPtr{};
//add virtual bases to the list, as long as we're on the same parent
std::unordered_set<const void*,
std::hash<const void*>, std::equal_to<const void*>,
pointer_utils::StdPolyAlloc<const void*>
> _virtualBases;
};
template <typename TBase>
class BaseClass {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
auto& resObj = static_cast<const TBase&>(obj);
if (auto ctx = ser.template contextOrNull<InheritanceContext>()) {
ctx->beginBase(obj, resObj);
fnc(ser, const_cast<TBase&>(resObj));
ctx->end();
} else {
fnc(ser, const_cast<TBase&>(resObj));
}
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
auto& resObj = static_cast<TBase&>(obj);
if (auto ctx = des.template contextOrNull<InheritanceContext>()) {
ctx->beginBase(obj, resObj);
fnc(des, resObj);
ctx->end();
} else {
fnc(des, resObj);
}
}
};
//requires InheritanceContext
template <typename TBase>
class VirtualBaseClass {
public:
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
auto& ctx = ser.template context<InheritanceContext>();
auto& resObj = static_cast<const TBase&>(obj);
if (ctx.beginVirtualBase(obj, resObj))
fnc(ser, const_cast<TBase&>(resObj));
ctx.end();
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
auto& ctx = des.template context<InheritanceContext>();
auto& resObj = static_cast<TBase&>(obj);
if (ctx.beginVirtualBase(obj, resObj))
fnc(des, resObj);
ctx.end();
}
};
}
namespace traits {
template<typename TBase, typename T>
struct ExtensionTraits<ext::BaseClass<TBase>, T> {
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
using TValue = TBase;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
template<typename TBase, typename T>
struct ExtensionTraits<ext::VirtualBaseClass<TBase>, T> {
static_assert(std::is_base_of<TBase, T>::value, "Invalid base class");
using TValue = TBase;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
//disable lambda overload, when serializing virtually inherited base class.
//Only one instance of virtual base will be serialized, when using multiple inheritance
//and it will be undefined behaviour if derived classes would have different virtual base class serialization flow.
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_INHERITANCE_H

View File

@@ -0,0 +1,220 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_POINTER_H
#define BITSERY_EXT_POINTER_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
namespace bitsery {
namespace ext {
namespace pointer_details {
template<typename T>
struct PtrOwnerManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T& obj) {
return obj;
}
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Owner;
}
static void create(T& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
obj = alloc.newObject<TElement>(typeId);
}
static void createPolymorphic(T& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
obj = static_cast<TElement*>(handler->create(alloc));
}
static void destroy(T& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
alloc.deleteObject(obj, typeId);
obj = nullptr;
}
static void destroyPolymorphic(T& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
handler->destroy(alloc, obj);
obj = nullptr;
}
};
template<typename T>
struct PtrObserverManager {
static_assert(std::is_pointer<T>::value, "");
using TElement = typename std::remove_pointer<T>::type;
static TElement* getPtr(T& obj) {
return obj;
}
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Observer;
}
//pure observer doesn't have create/createPolymorphic methods, but instead returns reference to pointer
//which gets updated later
static TElement*& getPtrRef(T& obj) {
return obj;
}
static void destroy(T& obj, MemResourceBase* , size_t ) {
obj = nullptr;
}
static void destroyPolymorphic(T& obj, MemResourceBase* , PolymorphicHandlerBase& ) {
obj = nullptr;
}
};
template<typename T>
struct NonPtrManager {
static_assert(!std::is_pointer<T>::value, "");
using TElement = T;
static TElement* getPtr(T& obj) {
return &obj;
}
static constexpr PointerOwnershipType getOwnership() {
return PointerOwnershipType::Owner;
}
// this code is unreachable for reference type, but is necessary to compile
// LCOV_EXCL_START
static void create(T& , MemResourceBase* , size_t ) {
}
static void createPolymorphic(T& , MemResourceBase* , PolymorphicHandlerBase& ) {
}
static void destroy(T& , MemResourceBase* , size_t ) {
}
static void destroyPolymorphic(T& , MemResourceBase* , PolymorphicHandlerBase& ) {
}
// LCOV_EXCL_STOP
};
// this class is used by NonPtrManager
struct NoRTTI {
template<typename TBase>
static size_t get(TBase&) {
return 0;
}
template<typename TBase>
static constexpr size_t get() {
return 0;
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return false;
}
};
}
template<typename RTTI>
using PointerOwnerBase = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrOwnerManager, PolymorphicContext, RTTI>;
using PointerOwner = PointerOwnerBase<StandardRTTI>;
using PointerObserver = pointer_utils::PointerObjectExtensionBase<
pointer_details::PtrObserverManager, PolymorphicContext, pointer_details::NoRTTI>;
//inherit from PointerObjectExtensionBase in order to specify PointerType::NotNull
class ReferencedByPointer : public pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI> {
public:
ReferencedByPointer() : pointer_utils::PointerObjectExtensionBase<
pointer_details::NonPtrManager, PolymorphicContext, pointer_details::NoRTTI>(
PointerType::NotNull) {}
};
}
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
template<typename T>
struct ExtensionTraits<ext::PointerObserver, T*> {
//although pointer observer doesn't serialize anything, but we still add value overload support to be consistent with pointer owners
//observer only writes/reads pointer id from pointer linking context
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
template<typename T>
struct ExtensionTraits<ext::ReferencedByPointer, T> {
//allow everything, because it is serialized as regular type, except it also creates pointerId that is required by NonOwningPointer to work
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_POINTER_H

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

@@ -0,0 +1,94 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_CHRONO_H
#define BITSERY_EXT_STD_CHRONO_H
#include "../traits/core/traits.h"
#include <chrono>
namespace bitsery {
namespace ext {
class StdDuration {
public:
template<typename Ser, typename T, typename Period, typename Fnc>
void serialize(Ser& ser, const std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
auto res = obj.count();
fnc(ser, res);
}
template<typename Des, typename T, typename Period, typename Fnc>
void deserialize(Des& des, std::chrono::duration<T, Period>& obj, Fnc&& fnc) const {
T res{};
fnc(des, res);
obj = std::chrono::duration<T, Period>{res};
}
};
class StdTimePoint {
public:
template<typename Ser, typename Clock, typename T, typename Period, typename Fnc>
void serialize(Ser& ser, const std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
auto res = obj.time_since_epoch().count();
fnc(ser, res);
}
template<typename Des, typename Clock, typename T, typename Period, typename Fnc>
void deserialize(Des& des, std::chrono::time_point<Clock, std::chrono::duration<T, Period>>& obj,
Fnc&& fnc) const {
T res{};
fnc(des, res);
auto dur = std::chrono::duration<T, Period>{res};
obj = std::chrono::time_point<Clock, std::chrono::duration<T, Period>>{dur};
}
};
}
namespace traits {
template<typename Rep, typename Period>
struct ExtensionTraits<ext::StdDuration, std::chrono::duration<Rep, Period>> {
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
template<typename Clock, typename Rep, typename Period>
struct ExtensionTraits<ext::StdTimePoint,
std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>>> {
using TValue = Rep;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_CHRONO_H

View File

@@ -0,0 +1,101 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_MAP_H
#define BITSERY_EXT_STD_MAP_H
#include "../traits/core/traits.h"
#include "../details/adapter_common.h"
#include "../details/serialization_common.h"
//we need this, so we could reserve for non ordered map
#include <unordered_map>
namespace bitsery {
namespace ext {
class StdMap {
public:
constexpr explicit StdMap(size_t maxSize):_maxSize{maxSize} {}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
using TKey = typename T::key_type;
using TValue = typename T::mapped_type;
auto size = obj.size();
assert(size <= _maxSize);
details::writeSize(ser.adapter(), size);
for (auto &v:obj)
fnc(ser, const_cast<TKey &>(v.first), const_cast<TValue &>(v.second));
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
using TKey = typename T::key_type;
using TValue = typename T::mapped_type;
size_t size{};
details::readSize(des.adapter(), size, _maxSize,
std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
obj.clear();
reserve(obj, size);
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
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));
}
}
private:
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename T>
void reserve(T& , size_t ) const {
//for ordered container do nothing
}
size_t _maxSize;
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdMap, T> {
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = false;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_STD_MAP_H

View File

@@ -0,0 +1,83 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_OPTIONAL_H
#define BITSERY_EXT_STD_OPTIONAL_H
#include "../traits/core/traits.h"
#include "../details/serialization_common.h"
#include <optional>
namespace bitsery {
namespace ext {
class StdOptional {
public:
/**
* Works with std::optional types
* @param alignBeforeData only makes sense when bit-packing enabled, by default aligns after writing/reading bool state of optional
*/
explicit StdOptional(bool alignBeforeData=true):_alignBeforeData{alignBeforeData} {}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const std::optional<T> &obj, Fnc &&fnc) const {
ser.boolValue(static_cast<bool>(obj));
if (_alignBeforeData)
ser.adapter().align();
if (obj)
fnc(ser, const_cast<T&>(*obj));
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, std::optional<T> &obj, Fnc &&fnc) const {
bool exists{};
des.boolValue(exists);
if (_alignBeforeData)
des.adapter().align();
if (exists) {
obj = ::bitsery::Access::create<T>();
fnc(des, *obj);
} else {
obj = std::nullopt;
}
}
private:
bool _alignBeforeData;
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdOptional, std::optional<T>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_STD_OPTIONAL_H

View File

@@ -0,0 +1,111 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_QUEUE_H
#define BITSERY_EXT_STD_QUEUE_H
#include <type_traits>
#include <queue>
//include type traits for deque and vector, because they are defaults for queue and priority_queue
#include "../traits/deque.h"
#include "../traits/vector.h"
namespace bitsery {
namespace ext {
class StdQueue {
private:
//inherit from queue so we could take underlying container
template <typename T, typename C>
struct QueueCnt : public std::queue<T, C>
{
static const C& getContainer(const std::queue<T, C>& s )
{
//get address of underlying container
return s.*(&QueueCnt::c);
}
static C& getContainer(std::queue<T, C>& s )
{
//get address of underlying container
return s.*(&QueueCnt::c);
}
};
//inherit from queue so we could take underlying container
template <typename T, typename Seq, typename Cmp>
struct PriorityQueueCnt : public std::priority_queue<T, Seq, Cmp>
{
static const Seq& getContainer(const std::priority_queue<T, Seq, Cmp>& s )
{
//get address of underlying container
return s.*(&PriorityQueueCnt::c);
}
static Seq& getContainer(std::priority_queue<T, Seq, Cmp>& s )
{
//get address of underlying container
return s.*(&PriorityQueueCnt::c);
}
};
size_t _maxSize;
public:
explicit StdQueue(size_t maxSize):_maxSize{maxSize} {};
//for queue
template<typename Ser, typename T, typename C, typename Fnc>
void serialize(Ser &ser, const std::queue<T,C> &obj, Fnc &&fnc) const {
ser.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Fnc>
void deserialize(Des &des, std::queue<T,C> &obj, Fnc &&fnc) const {
des.container(QueueCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
//for priority_queue
template<typename Ser, typename T, typename C, typename Comp, typename Fnc>
void serialize(Ser &ser, const std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
ser.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Comp, typename Fnc>
void deserialize(Des &des, std::priority_queue<T,C, Comp> &obj, Fnc &&fnc) const {
des.container(PriorityQueueCnt<T,C, Comp>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdQueue, T> {
using TValue = typename T::value_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_STD_QUEUE_H

View File

@@ -0,0 +1,100 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_SET_H
#define BITSERY_EXT_STD_SET_H
#include <cassert>
#include "../details/adapter_common.h"
#include "../details/serialization_common.h"
//we need this, so we could reserve for non ordered set
#include <unordered_set>
namespace bitsery {
namespace ext {
class StdSet {
public:
constexpr explicit StdSet(size_t maxSize):_maxSize{maxSize} {}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &obj, Fnc &&fnc) const {
using TKey = typename T::key_type;
auto size = obj.size();
assert(size <= _maxSize);
details::writeSize(ser.adapter(), size);
for (auto &v:obj)
fnc(ser, const_cast<TKey &>(v));
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &obj, Fnc &&fnc) const {
using TKey = typename T::key_type;
size_t size{};
details::readSize(des.adapter(), size, _maxSize, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
obj.clear();
reserve(obj, size);
auto hint = obj.begin();
for (auto i = 0u; i < size; ++i) {
auto key = bitsery::Access::create<TKey>();
fnc(des, key);
hint = obj.emplace_hint(hint, std::move(key));
}
}
private:
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_set<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename Key, typename Hash, typename KeyEqual, typename Allocator>
void reserve(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& obj, size_t size) const {
obj.reserve(size);
}
template <typename T>
void reserve(T& , size_t ) const {
//for ordered container do nothing
}
size_t _maxSize;
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::StdSet, T> {
using TValue = typename T::key_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_STD_SET_H

View File

@@ -0,0 +1,198 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_SMART_PTR_H
#define BITSERY_EXT_STD_SMART_PTR_H
#include <cassert>
#include "../traits/core/traits.h"
#include "utils/pointer_utils.h"
#include "utils/polymorphism_utils.h"
#include "utils/rtti_utils.h"
#include <memory>
namespace bitsery {
namespace ext {
namespace smart_ptr_details {
//further code is for managing shared ownership
//do not nest this type in pointer manager class itself, because it will be different type for different T
struct SharedPtrSharedState : pointer_utils::PointerSharedStateBase {
std::shared_ptr<void> obj{};
};
template<typename T>
struct SmartPtrOwnerManager {
using TElement = typename T::element_type;
template<typename TDeleter>
static TElement* getPtr(std::unique_ptr<TElement, TDeleter>& obj) {
return obj.get();
}
static TElement* getPtr(std::shared_ptr<TElement>& obj) {
return obj.get();
}
static TElement* getPtr(std::weak_ptr<TElement>& obj) {
if (auto ptr = obj.lock())
return ptr.get();
return nullptr;
}
static constexpr PointerOwnershipType getOwnership() {
return ::bitsery::details::IsSpecializationOf<T, std::unique_ptr>::value
? PointerOwnershipType::Owner
: std::is_same<std::shared_ptr<TElement>, T>::value
? PointerOwnershipType::SharedOwner
: PointerOwnershipType::SharedObserver;
}
template<typename TDeleter>
static void create(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolyAllocWithTypeId alloc,
size_t typeId) {
obj.reset(alloc.newObject<TElement>(typeId));
}
template<typename TDeleter>
static void createPolymorphic(std::unique_ptr<TElement, TDeleter>& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
obj.reset(static_cast<TElement*>(handler->create(alloc)));
}
template<typename TDel>
static void destroy(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolyAllocWithTypeId alloc, size_t typeId) {
auto ptr = obj.release();
alloc.deleteObject(ptr, typeId);
}
template<typename TDel>
static void destroyPolymorphic(std::unique_ptr<TElement, TDel>& obj, pointer_utils::PolyAllocWithTypeId alloc,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
auto ptr = obj.release();
handler->destroy(alloc, ptr);
}
static void destroy(std::shared_ptr<TElement>& obj, MemResourceBase*, size_t) {
obj.reset();
}
static void destroyPolymorphic(std::shared_ptr<TElement>& obj, MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&) {
obj.reset();
}
static void destroy(std::weak_ptr<TElement>& obj, MemResourceBase*, size_t) {
obj.reset();
}
static void destroyPolymorphic(std::weak_ptr<TElement>& obj, MemResourceBase*,
const std::shared_ptr<PolymorphicHandlerBase>&) {
obj.reset();
}
// define a type that will store shared state for shared and weak ptrs
using TSharedState = SharedPtrSharedState;
static void createShared(TSharedState& state,
std::shared_ptr<TElement>& obj, MemResourceBase* memResource, size_t typeId) {
// capture deleter parameters by value
pointer_utils::PolyAllocWithTypeId alloc{memResource};
obj.reset(alloc.newObject<TElement>(typeId), [alloc, typeId](TElement* data) {
alloc.deleteObject(data, typeId);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
}
static void createSharedPolymorphic(TSharedState& state,
std::shared_ptr<TElement>& obj, MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
// capture deleter parameters by value
pointer_utils::PolyAllocWithTypeId alloc{memResource};
obj.reset(static_cast<TElement*>(handler->create(alloc)), [alloc, handler](TElement* data) {
handler->destroy(alloc, data);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
state.obj = obj;
}
static void createShared(TSharedState& state,
std::weak_ptr<TElement>& obj, MemResourceBase* memResource, size_t typeId) {
pointer_utils::PolyAllocWithTypeId alloc{memResource};
std::shared_ptr<TElement> res(alloc.newObject<TElement>(typeId),[alloc, typeId](TElement* data) {
alloc.deleteObject(data, typeId);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
}
static void createSharedPolymorphic(TSharedState& state,
std::weak_ptr<TElement>& obj, MemResourceBase* memResource,
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
pointer_utils::PolyAllocWithTypeId alloc{memResource};
std::shared_ptr<TElement> res(static_cast<TElement*>(handler->create(alloc)),
[alloc, handler](TElement* data) {
handler->destroy(alloc, data);
}, pointer_utils::StdPolyAlloc<TElement>(memResource));
obj = res;
state.obj = res;
}
static void saveToSharedState(TSharedState& state, T& obj) {
state.obj = std::shared_ptr<TElement>(obj);
}
static void loadFromSharedState(TSharedState& state, T& obj) {
//reinterpret_pointer_cast is only since c++17
auto p = reinterpret_cast<TElement*>(state.obj.get());
obj = std::shared_ptr<TElement>(state.obj, p);
}
};
}
template<typename RTTI>
using StdSmartPtrBase = pointer_utils::PointerObjectExtensionBase<
smart_ptr_details::SmartPtrOwnerManager, PolymorphicContext, RTTI>;
//helper type for convienience
using StdSmartPtr = StdSmartPtrBase<StandardRTTI>;
}
namespace traits {
template<typename T, typename RTTI>
struct ExtensionTraits<ext::StdSmartPtrBase<RTTI>, T> {
using TValue = typename T::element_type;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
//if underlying type is not polymorphic, then we can enable lambda syntax
static constexpr bool SupportLambdaOverload = !RTTI::template isPolymorphic<TValue>();
};
}
}
#endif //BITSERY_EXT_STD_SMART_PTR_H

View File

@@ -0,0 +1,82 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_STACK_H
#define BITSERY_EXT_STD_STACK_H
#include <type_traits>
#include <stack>
//include type traits for deque, because stack default underlying container is deque
#include "../traits/deque.h"
namespace bitsery {
namespace ext {
class StdStack {
private:
//inherit from stack so we could take underlying container
template <typename T, typename C>
struct StackCnt : public std::stack<T, C>
{
static const C& getContainer(const std::stack<T, C>& s )
{
//get address of underlying container
return s.*(&StackCnt::c);
}
static C& getContainer(std::stack<T, C>& s )
{
//get address of underlying container
return s.*(&StackCnt::c);
}
};
size_t _maxSize;
public:
explicit StdStack(size_t maxSize):_maxSize{maxSize} {};
template<typename Ser, typename T, typename C, typename Fnc>
void serialize(Ser &ser, const std::stack<T,C> &obj, Fnc &&fnc) const {
ser.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
template<typename Des, typename T, typename C, typename Fnc>
void deserialize(Des &des, std::stack<T,C> &obj, Fnc &&fnc) const {
des.container(StackCnt<T,C>::getContainer(obj), _maxSize, std::forward<Fnc>(fnc));
}
};
}
namespace traits {
template<typename T, typename Seq>
struct ExtensionTraits<ext::StdStack, std::stack<T, Seq>> {
using TValue = T;
static constexpr bool SupportValueOverload = true;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = true;
};
}
}
#endif //BITSERY_EXT_STD_STACK_H

View File

@@ -0,0 +1,81 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_TUPLE_H
#define BITSERY_EXT_STD_TUPLE_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include <tuple>
namespace bitsery {
namespace ext {
template<typename ...Overloads>
class StdTuple : public details::CompositeTypeOverloadsUtils<std::tuple, Overloads...> {
public:
template<typename Ser, typename Fnc, typename ...Ts>
void serialize(Ser& ser, const std::tuple<Ts...>& obj, Fnc&&) const {
serializeAll(ser, const_cast<std::tuple<Ts...>&>(obj));
}
template<typename Des, typename Fnc, typename ...Ts>
void deserialize(Des& des, std::tuple<Ts...>& obj, Fnc&&) const {
serializeAll(des, obj);
}
private:
template<typename S, typename ...Ts>
void serializeAll(S& s, std::tuple<Ts...>& obj) const {
this->execAll(obj, [this, &s](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(s, std::get<Index>(data));
});
}
};
// deduction guide
template<typename ...Overloads>
StdTuple(Overloads...) -> StdTuple<Overloads...>;
}
namespace traits {
template<typename Tuple, typename ... Overloads>
struct ExtensionTraits<ext::StdTuple<Overloads...>, Tuple> {
static_assert(bitsery::details::IsSpecializationOf<Tuple, std::tuple>::value,
"StdTuple only works with std::tuple");
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_TUPLE_H

View File

@@ -0,0 +1,91 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_STD_VARIANT_H
#define BITSERY_EXT_STD_VARIANT_H
#include "utils/composite_type_overloads.h"
#include "../traits/core/traits.h"
#include <variant>
namespace bitsery {
namespace ext {
template<typename ...Overloads>
class StdVariant : public details::CompositeTypeOverloadsUtils<std::variant, Overloads...> {
public:
template<typename Ser, typename Fnc, typename ...Ts>
void serialize(Ser& ser, const std::variant<Ts...>& obj, Fnc&&) const {
auto index = obj.index();
assert(index != std::variant_npos);
details::writeSize(ser.adapter(), index);
this->execIndex(index, const_cast<std::variant<Ts...>&>(obj), [this, &ser](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
this->serializeType(ser, std::get<Index>(data));
});
}
template<typename Des, typename Fnc, typename ...Ts>
void deserialize(Des& des, std::variant<Ts...>& obj, Fnc&&) const {
size_t index{};
details::readSize(des.adapter(), index, sizeof...(Ts), std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
this->execIndex(index, obj, [this, &des](auto& data, auto index) {
constexpr size_t Index = decltype(index)::value;
using TElem = typename std::variant_alternative<Index, std::variant<Ts...>>::type;
TElem item = ::bitsery::Access::create<TElem>();
this->serializeType(des, item);
data = std::variant<Ts...>(std::in_place_index_t<Index>{}, std::move(item));
});
}
};
// deduction guide
template<typename ...Overloads>
StdVariant(Overloads...) -> StdVariant<Overloads...>;
}
//defines empty fuction, that handles monostate
template <typename S>
void serialize(S& , std::monostate&) {}
namespace traits {
template<typename Variant, typename ... Overloads>
struct ExtensionTraits<ext::StdVariant<Overloads...>, Variant> {
static_assert(bitsery::details::IsSpecializationOf<Variant, std::variant>::value,
"StdVariant only works with std::variant");
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_STD_VARIANT_H

View File

@@ -0,0 +1,136 @@
//MIT License
//
//Copyright (c) 2019 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
#define BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H
#include "../../details/serialization_common.h"
#include <functional>
#if __cplusplus < 201703L
#error these utils requires c++17
// in theory, it could be implemented using C++11
// but without class template argument deduction guides that would be very inconvenient to use
// these are very helpul for sum types (e.g. std::variant),
// but for product types (e.g. std::tuple) you can you can easily do it your self with lambda, without extension
#endif
namespace bitsery {
namespace ext {
// might be usable, when you want to have one overload set for different composite types,
// e.g. variant, tuple and pair
template<class... Ts>
struct CompositeTypeOverloads : Ts ... {
using Ts::operator()...;
};
template<typename ...Overloads>
CompositeTypeOverloads(Overloads...) -> CompositeTypeOverloads<Overloads...>;
// convenient way to invoke s.value<N>, shorter than specifying a lambda
template<typename T, size_t N>
struct OverloadValue {
template <typename S>
void operator()(S& s, T& v) const {
s.template value<N>(v);
}
};
// convenient way to invoke other extension using value or object overloads
// there is no reason to write OverloadExtLambda,
// because you'll need to specify lambda type, which is very inconvenient and it will be much
// easier to simple write a lambda with extension inside it,
// in order to implement it in a convenient way, i need a way to deduce only last template parameter (lambda type)
// but this is not possible with deduction guides at the moment
template<typename T, size_t N, typename Ext>
struct OverloadExtValue : public Ext {
template <typename S>
void operator()(S& s, T& v) const {
s.template ext<N>(v, static_cast<const Ext&>(*this));
}
};
template<typename T, typename Ext>
struct OverloadExtObject : public Ext {
template <typename S>
void operator()(S& s, T& v) const {
s.ext(v, static_cast<const Ext&>(*this));
}
};
}
namespace details {
template<template<typename ...> typename CompositeType, typename ...Overloads>
class CompositeTypeOverloadsUtils : public ext::CompositeTypeOverloads<Overloads...> {
protected:
// converts run-time index to compile-time index,
// by calling lambda with std::integral_constant<size_t, INDEX>
template<typename Fnc, typename ... Ts>
void execIndex(size_t index, CompositeType<Ts...>& obj, Fnc&& fnc) const {
execIndexImpl(index, obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// call lambda for all indexes in composite type
template<typename Fnc, typename ... Ts>
void execAll(CompositeType<Ts...>& obj, Fnc&& fnc) const {
execAllImpl(obj, std::forward<Fnc>(fnc), std::index_sequence_for<Ts...>{});
}
// serialize a type, by using overload first
template<typename S, typename T>
void serializeType(S& s, T& v) const {
// first check if overload exists, otherwise try to call serialize method
if constexpr (hasOverload<S, T>()) {
std::invoke(*this, s, v);
} else {
static_assert(details::SerializeFunction<S, T>::isDefined(),
"Please define overload or 'serialize' function for your type.");
s.object(v);
}
}
private:
template<typename S, typename T>
static constexpr bool hasOverload() {
return std::is_invocable<ext::CompositeTypeOverloads<Overloads...>,
std::add_lvalue_reference_t<S>, std::add_lvalue_reference_t<T>>::value;
}
template<typename Variant, typename Fnc, size_t ...Is>
void execIndexImpl(size_t index, Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
((index == Is ? fnc(obj, std::integral_constant<size_t, Is>{}), 0 : 0), ...);
}
template<typename Variant, typename Fnc, size_t ...Is>
void execAllImpl(Variant& obj, Fnc&& fnc, std::index_sequence<Is...>) const {
(fnc(obj, std::integral_constant<size_t, Is>{}), ...);
}
};
}
}
#endif //BITSERY_EXT_COMPOSITE_TYPE_OVERLOADS_H

View File

@@ -0,0 +1,171 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_MEMORY_RESOURCE_H
#define BITSERY_EXT_MEMORY_RESOURCE_H
#include "../../details/serialization_common.h"
#include <new>
namespace bitsery {
namespace ext {
// these are very similar to c++17 polymorphic allocator and memory resource classes
// but i don't want to enforce users to use c++17 if they want to use pointers
// plus this has additional information from RTTI about runtime type information,
// might be useful working with polymorphic types.
// The same memory resource is used to allocate internal data in various contexts,
// (typeId is always 0 for internal data allocation in contexts).
class MemResourceBase {
public:
virtual void* allocate(size_t bytes, size_t alignment, size_t typeId) = 0;
virtual void deallocate(void* ptr, size_t bytes, size_t alignment, size_t typeId) noexcept = 0;
virtual ~MemResourceBase() noexcept = default;
};
// default implementation for MemResourceBase using new and delete
class MemResourceNewDelete final: public MemResourceBase {
public:
inline void* allocate(size_t bytes, size_t /*alignment*/, size_t /*typeId*/) final {
return (::operator new(bytes));
}
inline void
deallocate(void* ptr, size_t /*bytes*/, size_t /*alignment*/, size_t /*typeId*/) noexcept final {
(::operator delete(ptr));
}
~MemResourceNewDelete() noexcept final = default;
};
// these classes are used internally by bitsery extensions and and pointer utils
namespace pointer_utils {
// this is helper class that stores memory resource and knows how to construct/destroy objects
// capture this by value for custom deleters, because during deserialization mem resource can be changed
class PolyAllocWithTypeId final {
public:
constexpr PolyAllocWithTypeId(MemResourceBase* memResource = nullptr)
:_resource{memResource} {}
template<typename T>
T* allocate(size_t n, size_t typeId) const {
const auto bytes = sizeof(T) * n;
constexpr auto alignment = std::alignment_of<T>::value;
void* ptr = _resource
? _resource->allocate(bytes, alignment, typeId)
: ext::MemResourceNewDelete{}.allocate(bytes, alignment, typeId);
return static_cast<T*>(ptr);
}
template<typename T>
void deallocate(T* ptr, size_t n, size_t typeId) const noexcept {
const auto bytes = sizeof(T) * n;
constexpr auto alignment = std::alignment_of<T>::value;
_resource
? _resource->deallocate(ptr, bytes, alignment, typeId)
: ext::MemResourceNewDelete{}.deallocate(ptr, bytes, alignment, typeId);
}
template<typename T>
T* newObject(size_t typeId) const {
auto ptr = allocate<T>(1, typeId);
return ::bitsery::Access::create<T>(ptr);
}
template<typename T>
void deleteObject(T* obj, size_t typeId) const {
obj->~T();
deallocate(obj, 1, typeId);
}
void setMemResource(ext::MemResourceBase* resource) {
_resource = resource;
}
ext::MemResourceBase* getMemResource() const {
return _resource;
}
bool operator==(const PolyAllocWithTypeId& rhs) const noexcept {
return _resource == rhs._resource;
}
bool operator!=(const PolyAllocWithTypeId& rhs) const noexcept {
return !(*this == rhs);
}
private:
ext::MemResourceBase* _resource;
};
// this is very similar to c++17 PolymorphicAllocator
// it just wraps our PolyAllocWithTypeId and pass 0 as typeId
// and defines core functions for c++ Allocator concept,
template<class T>
class StdPolyAlloc {
public:
using value_type = T;
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
:_alloc{memResource} {}
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
template <typename U>
friend class StdPolyAlloc;
template<class U>
constexpr explicit StdPolyAlloc(const StdPolyAlloc<U>& other) noexcept
:_alloc{other._alloc} {
}
T* allocate(std::size_t n) {
return _alloc.allocate<T>(n, 0);
}
void deallocate(T* p, std::size_t n) noexcept {
return _alloc.deallocate(p, n, 0);
}
template<class U>
friend bool operator==(const StdPolyAlloc<T>& lhs,
const StdPolyAlloc<U>& rhs) noexcept {
return lhs._alloc == rhs._alloc;
}
template<class U>
friend bool operator!=(const StdPolyAlloc<T>& lhs,
const StdPolyAlloc<U>& rhs) noexcept {
return !(lhs == rhs);
}
private:
PolyAllocWithTypeId _alloc;
};
}
}
}
#endif //BITSERY_EXT_MEMORY_RESOURCE_H

View File

@@ -0,0 +1,487 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_POINTER_UTILS_H
#define BITSERY_POINTER_UTILS_H
#include <unordered_map>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>
#include "polymorphism_utils.h"
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {
//change name
enum class PointerType : uint8_t {
Nullable,
NotNull
};
// Observer - not responsible for pointer lifetime management.
// Owner - only ONE owner is responsible for this pointers creation/destruction
// SharedOwner, SharedObserver - MANY shared owners is responsible for pointer creation/destruction
// requires additional context to manage shared owners themselves.
// SharedOwner actually manages life time e.g. std::shared_ptr
// SharedObserver do not manage life time of the pointer, but can observe shared state .e.. std::weak_ptr
// and differently from Observer, creates new object if necessary and saves to shared state
enum class PointerOwnershipType : uint8_t {
Observer,
Owner,
SharedOwner,
SharedObserver
};
namespace pointer_utils {
//this class is used to store context for shared ptr owners
struct PointerSharedStateBase {
virtual ~PointerSharedStateBase() = default;
};
struct PointerSharedStateDeleter {
PointerSharedStateDeleter() = default;
explicit PointerSharedStateDeleter(MemResourceBase* memResource)
:_memResource{memResource} {}
void operator()(PointerSharedStateBase* data) const {
data->~PointerSharedStateBase();
StdPolyAlloc<PointerSharedStateBase> alloc{_memResource};
alloc.deallocate(data, 1);
}
MemResourceBase* _memResource = nullptr;
};
//PLC info is internal classes for serializer, and deserializer
struct PLCInfo {
explicit PLCInfo(PointerOwnershipType ownershipType_)
: ownershipType{ownershipType_},
isSharedProcessed{false} {};
PointerOwnershipType ownershipType;
bool isSharedProcessed;
void update(PointerOwnershipType ptrType) {
//do nothing for observer
if (ptrType == PointerOwnershipType::Observer)
return;
if (ownershipType == PointerOwnershipType::Observer) {
//set ownership type
ownershipType = ptrType;
return;
}
//only shared ownership can get here multiple times
assert(ptrType == PointerOwnershipType::SharedOwner ||
ptrType == PointerOwnershipType::SharedObserver);
//check if need to update to SharedOwner
if (ptrType == PointerOwnershipType::SharedOwner)
ownershipType = ptrType;
//mark that object already processed, so we do not serialize/deserialize duplicate objects
isSharedProcessed = true;
}
};
struct PLCInfoSerializer : PLCInfo {
PLCInfoSerializer(size_t id_, PointerOwnershipType ownershipType_)
: PLCInfo(ownershipType_), id{id_} {}
size_t id;
};
struct PLCInfoDeserializer : PLCInfo {
PLCInfoDeserializer(void* ptr, PointerOwnershipType ownershipType_, MemResourceBase* memResource_)
: PLCInfo(ownershipType_),
ownerPtr{ptr},
memResource{memResource_},
observersList{StdPolyAlloc<std::reference_wrapper<void*>>{memResource_}} {};
//need to override these explicitly because we have pointer member
PLCInfoDeserializer(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer(PLCInfoDeserializer&&) = default;
PLCInfoDeserializer& operator=(const PLCInfoDeserializer&) = delete;
PLCInfoDeserializer& operator=(PLCInfoDeserializer&&) = default;
void processOwner(void* ptr) {
ownerPtr = ptr;
assert(ownershipType != PointerOwnershipType::Observer);
for (auto& o:observersList)
o.get() = ptr;
observersList.clear();
observersList.shrink_to_fit();
}
void processObserver(void* (& ptr)) {
if (ownerPtr) {
ptr = ownerPtr;
} else {
observersList.emplace_back(ptr);
}
}
void* ownerPtr;
MemResourceBase* memResource;
std::vector<std::reference_wrapper<void*>,
StdPolyAlloc<std::reference_wrapper<void*>>> observersList;
std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter> sharedState{};
};
class PointerLinkingContextSerialization {
public:
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
: _currId{0},
_ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization& operator=(const PointerLinkingContextSerialization&) = delete;
PointerLinkingContextSerialization(PointerLinkingContextSerialization&&) = default;
PointerLinkingContextSerialization& operator=(PointerLinkingContextSerialization&&) = default;
~PointerLinkingContextSerialization() = default;
const PLCInfoSerializer& getInfoByPtr(const void* ptr, PointerOwnershipType ptrType) {
auto res = _ptrMap.emplace(ptr, PLCInfoSerializer{_currId + 1u, ptrType});
auto& ptrInfo = res.first->second;
if (res.second) {
++_currId;
return ptrInfo;
}
ptrInfo.update(ptrType);
return ptrInfo;
}
//valid, when all pointers have owners.
//we cannot serialize pointers, if we haven't serialized objects themselves
bool isPointerSerializationValid() const {
return std::all_of(_ptrMap.begin(), _ptrMap.end(),
[](const std::pair<const void*, PLCInfoSerializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
private:
size_t _currId;
std::unordered_map<const void*, PLCInfoSerializer,
std::hash<const void*>, std::equal_to<const void*>,
StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>
> _ptrMap;
};
class PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
: _memResource{memResource},
_idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization& operator=(const PointerLinkingContextDeserialization&) = delete;
PointerLinkingContextDeserialization(PointerLinkingContextDeserialization&&) = default;
PointerLinkingContextDeserialization& operator=(PointerLinkingContextDeserialization&&) = default;
~PointerLinkingContextDeserialization() = default;
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType) {
auto res = _idMap.emplace(id, PLCInfoDeserializer{nullptr, ptrType, _memResource});
auto& ptrInfo = res.first->second;
if (!res.second)
ptrInfo.update(ptrType);
return ptrInfo;
}
void clearSharedState() {
for (auto& item: _idMap)
item.second.sharedState.reset();
}
//valid, when all pointers has owners
bool isPointerDeserializationValid() const {
return std::all_of(_idMap.begin(), _idMap.end(),
[](const std::pair<const size_t, PLCInfoDeserializer>& p) {
return p.second.ownershipType == PointerOwnershipType::SharedOwner ||
p.second.ownershipType == PointerOwnershipType::Owner;
});
}
MemResourceBase* getMemResource() noexcept {
return _memResource;
}
void setMemResource(MemResourceBase* resource) noexcept {
_memResource = resource;
}
private:
MemResourceBase* _memResource;
std::unordered_map<size_t, PLCInfoDeserializer,
std::hash<size_t>, std::equal_to<size_t>,
StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>> _idMap;
};
}
//this class is for convenience
class PointerLinkingContext :
public pointer_utils::PointerLinkingContextSerialization,
public pointer_utils::PointerLinkingContextDeserialization {
public:
explicit PointerLinkingContext(MemResourceBase* memResource = nullptr)
:pointer_utils::PointerLinkingContextSerialization(memResource),
pointer_utils::PointerLinkingContextDeserialization(memResource) {};
bool isValid() const {
return isPointerSerializationValid() && isPointerDeserializationValid();
}
};
namespace pointer_utils {
template<template<typename> class TPtrManager,
template<typename> class TPolymorphicContext, typename RTTI>
class PointerObjectExtensionBase {
public:
// helper types
template<typename T>
struct IsPolymorphic : std::integral_constant<bool,
RTTI::template isPolymorphic<typename TPtrManager<T>::TElement>()> {
};
template<PointerOwnershipType Value>
using OwnershipType = std::integral_constant<PointerOwnershipType, Value>;
explicit PointerObjectExtensionBase(PointerType ptrType = PointerType::Nullable,
MemResourceBase* resource = nullptr,
bool resourcePropagate = false) :
_ptrType{ptrType},
_resourcePropagate{resourcePropagate},
_resource{resource} {
}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser& ser, const T& obj, Fnc&& fnc) const {
auto ptr = TPtrManager<T>::getPtr(const_cast<T&>(obj));
if (ptr) {
auto& ctx = ser.template context<pointer_utils::PointerLinkingContextSerialization>();
auto& ptrInfo = ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
details::writeSize(ser.adapter(), ptrInfo.id);
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
if (!ptrInfo.isSharedProcessed)
serializeImpl(ser, ptr, std::forward<Fnc>(fnc), IsPolymorphic<T>{});
}
} else {
assert(_ptrType == PointerType::Nullable);
details::writeSize(ser.adapter(), 0);
}
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des& des, T& obj, Fnc&& fnc) const {
size_t id{};
details::readSize(des.adapter(), id, 0, std::false_type{});
auto& ctx = des.template context<pointer_utils::PointerLinkingContextDeserialization>();
auto prevResource = ctx.getMemResource();
auto memResource = _resource ? _resource : prevResource;
// if we have resource and propagate is true, then change current resource
// so that deserializing nested pointers it will be used
if (_resource && _resourcePropagate) {
ctx.setMemResource(memResource);
}
if (id) {
auto& ptrInfo = ctx.getInfoById(id, TPtrManager<T>::getOwnership());
deserializeImpl(memResource, ptrInfo, des, obj, std::forward<Fnc>(fnc), IsPolymorphic<T>{},
OwnershipType<TPtrManager<T>::getOwnership()>{});
} else {
if (_ptrType == PointerType::Nullable) {
if (auto ptr = TPtrManager<T>::getPtr(obj)) {
destroyPtr(memResource, des, obj, IsPolymorphic<T>{});
};
} else
des.adapter().error(ReaderError::InvalidPointer);
}
if (_resource && _resourcePropagate) {
ctx.setMemResource(prevResource);
}
}
private:
template<typename Des, typename TObj>
void destroyPtr(MemResourceBase* memResource, Des& des, TObj& obj,
std::true_type /*polymorphic*/) const {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
auto ptr = TPtrManager<TObj>::getPtr(obj);
TPtrManager<TObj>::destroyPolymorphic(obj, memResource, ctx.getPolymorphicHandler(*ptr));
}
template<typename Des, typename TObj>
void destroyPtr(MemResourceBase* memResource, Des&, TObj& obj,
std::false_type /*polymorphic*/) const {
TPtrManager<TObj>::destroy(obj, memResource, RTTI::template get<typename TPtrManager<TObj>::TElement>());
}
template<typename T>
const void* getBasePtr(const T* ptr) const {
// todo implement handling of types with virtual inheritance
// this is required to correctly track same object, when one object is derived and other is base class
// e.g. shared_ptr<Base> and weak_ptr<Derived> or pointer observer Base*
return ptr;
}
template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const {
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
ctx.serialize(ser, *ptr);
}
template<typename Ser, typename TPtr, typename Fnc>
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&& fnc, std::false_type) const {
fnc(ser, *ptr);
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
std::true_type, OwnershipType<PointerOwnershipType::Owner>) const {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize(des, TPtrManager<T>::getPtr(obj),
[&obj, this, memResource](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::createPolymorphic(obj, memResource, handler);
return TPtrManager<T>::getPtr(obj);
},
[&obj, memResource, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
std::false_type, OwnershipType<PointerOwnershipType::Owner>) const {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
fnc(des, *ptr);
} else {
TPtrManager<T>::create(obj, memResource, RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj);
fnc(des, *ptr);
}
ptrInfo.processOwner(ptr);
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&&,
std::true_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
if (!ptrInfo.sharedState) {
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
ctx.deserialize(des, TPtrManager<T>::getPtr(obj),
[&obj, &ptrInfo, memResource, this](
const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::createSharedPolymorphic(
createAndGetSharedStateObj<T>(ptrInfo),
obj, memResource, handler);
return TPtrManager<T>::getPtr(obj);
},
[&obj, memResource, this](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
});
if (!ptrInfo.sharedState)
TPtrManager<T>::saveToSharedState(createAndGetSharedStateObj<T>(ptrInfo), obj);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj, Fnc&& fnc,
std::false_type, OwnershipType<PointerOwnershipType::SharedOwner>) const {
if (!ptrInfo.sharedState) {
auto ptr = TPtrManager<T>::getPtr(obj);
if (ptr) {
TPtrManager<T>::saveToSharedState(createAndGetSharedStateObj<T>(ptrInfo), obj);
} else {
TPtrManager<T>::createShared(
createAndGetSharedStateObj<T>(ptrInfo),
obj, memResource, RTTI::template get<typename TPtrManager<T>::TElement>());
ptr = TPtrManager<T>::getPtr(obj);
}
fnc(des, *ptr);
}
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
}
template<typename Des, typename T, typename Fnc, typename isPolymorph>
void deserializeImpl(MemResourceBase* memResource, PLCInfoDeserializer& ptrInfo, Des& des, T& obj,
Fnc&& fnc, isPolymorph polymorph,
OwnershipType<PointerOwnershipType::SharedObserver>) const {
deserializeImpl(memResource, ptrInfo, des, obj, fnc, polymorph,
OwnershipType<PointerOwnershipType::SharedOwner>{});
}
template<typename Des, typename T, typename Fnc, typename isPolymorphic>
void deserializeImpl(MemResourceBase* , PLCInfoDeserializer& ptrInfo, Des&, T& obj, Fnc&&,
isPolymorphic, OwnershipType<PointerOwnershipType::Observer>) const {
ptrInfo.processObserver(reinterpret_cast<void*&>(TPtrManager<T>::getPtrRef(obj)));
}
template <typename T>
typename TPtrManager<T>::TSharedState& createAndGetSharedStateObj(PLCInfoDeserializer& info) const {
using TSharedState = typename TPtrManager<T>::TSharedState;
StdPolyAlloc<TSharedState> alloc {info.memResource};
auto* ptr = alloc.allocate(1);
auto* obj = new (ptr)TSharedState{};
info.sharedState = std::unique_ptr<PointerSharedStateBase, PointerSharedStateDeleter>(
obj, PointerSharedStateDeleter{info.memResource});
return *obj;
}
template <typename T>
typename TPtrManager<T>::TSharedState& getSharedStateObj(PLCInfoDeserializer& info) const {
return static_cast<typename TPtrManager<T>::TSharedState&>(*info.sharedState);
}
PointerType _ptrType;
bool _resourcePropagate;
MemResourceBase* _resource;
};
}
}
}
#endif //BITSERY_POINTER_UTILS_H

View File

@@ -0,0 +1,287 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_POLYMORPHISM_UTILS_H
#define BITSERY_EXT_POLYMORPHISM_UTILS_H
#include <unordered_map>
#include <memory>
#include "memory_resource.h"
#include "../../details/adapter_common.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace ext {
//helper type, that contains list of types
template<typename ...>
struct PolymorphicClassesList {
};
//specialize for your base class by deriving from PolymorphicDerivedClasses with list of derivatives that DIRECTLY inherits from your base class.
//e.g.
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
// template <> PolymorphicBaseClass<Dog>: PolymorphicDerivedClasses<Bulldog, GoldenRetriever> {};
// IMPORTANT !!!
// although you can add all derivates to same base like this:
// template <> PolymorphicBaseClass<Animal>:PolymorphicDerivedClasses<Dog, Cat, Bulldog, GoldenRetriever>{};
// it will not work when you try to serialize Dog*, because it will not find Bulldog and GoldenRetriever
template<typename TBase>
struct PolymorphicBaseClass {
using Childs = PolymorphicClassesList<>;
};
//derive from this class when specifying childs for your base class, atleast one child must exists, hence T1
//e.g.
// template <> PolymorphicBaseClass<Animal>: PolymorphicDerivedClasses<Dog, Cat>{};
template<typename T1, typename ... Tn>
struct PolymorphicDerivedClasses {
using Childs = PolymorphicClassesList<T1, Tn...>;
};
class PolymorphicHandlerBase {
public:
virtual void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const = 0;
virtual void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const = 0;
virtual void process(void* ser, void* obj) const = 0;
virtual ~PolymorphicHandlerBase() = default;
};
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
class PolymorphicHandler : public PolymorphicHandlerBase {
public:
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const final {
return toBase(alloc.newObject<TDerived>(RTTI::template get<TDerived>()));
}
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const final {
alloc.deleteObject<TDerived>(fromBase(ptr), RTTI::template get<TDerived>());
}
void process(void* ser, void* obj) const final {
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
}
private:
TDerived* fromBase(void* obj) const {
return RTTI::template cast<TBase, TDerived>(static_cast<TBase*>(obj));
}
TBase* toBase(void* obj) const {
return RTTI::template cast<TDerived, TBase>(static_cast<TDerived*>(obj));
}
};
template<typename RTTI>
class PolymorphicContext {
private:
struct BaseToDerivedKey {
std::size_t baseHash;
std::size_t derivedHash;
bool operator==(const BaseToDerivedKey& other) const {
return baseHash == other.baseHash && derivedHash == other.derivedHash;
}
};
struct BaseToDerivedKeyHashier {
size_t operator()(const BaseToDerivedKey& key) const {
return (key.baseHash + (key.baseHash << 6) + (key.derivedHash >> 2)) ^ key.derivedHash;
}
};
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void add() {
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
addChilds<TSerializer, THierarchy, TBase, TDerived>(typename THierarchy<TDerived>::Childs{});
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived, typename T1, typename ... Tn>
void addChilds(PolymorphicClassesList<T1, Tn...>) {
static_assert(std::is_base_of<TDerived, T1>::value,
"PolymorphicBaseClass<TBase> must derive a list of derived classes from TBase.");
add<TSerializer, THierarchy, TBase, T1>();
addChilds<TSerializer, THierarchy, TBase, TDerived>(PolymorphicClassesList<Tn...>{});
//iterate through derived class hierarchy as well
add<TSerializer, THierarchy, T1, T1>();
}
template<typename TSerializer, template<typename> class THierarchy, typename TBase, typename TDerived>
void addChilds(PolymorphicClassesList<>) {
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::false_type) {
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>;
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{}, [alloc](THandler* data) mutable {
data->~THandler();
alloc.deallocate(data, 1);
}, alloc);
if (_baseToDerivedMap
.emplace(key, std::move(handler))
.second) {
auto it = _baseToDerivedArray.find(key.baseHash);
if (it == _baseToDerivedArray.end()) {
it = _baseToDerivedArray.emplace(
std::piecewise_construct,
std::forward_as_tuple(key.baseHash),
std::forward_as_tuple(pointer_utils::StdPolyAlloc<size_t>{_memResource})).first;
}
it->second.push_back(key.derivedHash);
}
}
template<typename TSerializer, typename TBase, typename TDerived>
void addToMap(std::true_type) {
//cannot add abstract class
}
MemResourceBase* _memResource;
// store shared ptr to polymorphic handler, because it might be copied to "smart pointer" deleter
std::unordered_map<BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>,
BaseToDerivedKeyHashier, std::equal_to<BaseToDerivedKey>,
pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>
> _baseToDerivedMap;
// this will allow convert from platform specific type information, to platform independent base->derived index
// this only works if all polymorphic relationships (PolymorphicBaseClass<TBase> -> PolymorphicDerivedClasses<TDerived...>)
// is equal between platforms.
std::unordered_map<size_t, std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>,
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>>>>
> _baseToDerivedArray;
public:
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}}
{}
PolymorphicContext(const PolymorphicContext& ) = delete;
PolymorphicContext& operator = (const PolymorphicContext&) = delete;
PolymorphicContext(PolymorphicContext&& ) = default;
PolymorphicContext& operator = (PolymorphicContext&&) = default;
void clear() {
_baseToDerivedMap.clear();
_baseToDerivedArray.clear();
}
// THierarchy is the name of class, that defines hierarchy
// PolymorphicBaseClass is defined as default parameter, so that at instantiation time
// it will get unique symbol in translation unit for PolymorphicBaseClass (which is defined in anonymous namespace)
// https://github.com/fraillt/bitsery/issues/9
template<typename TSerializer, template<typename> class THierarchy = PolymorphicBaseClass, typename T1, typename ...Tn>
void registerBasesList(PolymorphicClassesList<T1, Tn...>) {
add<TSerializer, THierarchy, T1, T1>();
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
}
template<typename TSerializer, template<typename> class THierarchy>
void registerBasesList(PolymorphicClassesList<>) {
}
// optional method, in case you want to construct base class hierarchy your self
template<typename TSerializer, typename TBase, typename TDerived>
void registerSingleBaseBranch() {
static_assert(std::is_base_of<TBase, TDerived>::value, "TDerived must be derived from TBase");
static_assert(!std::is_abstract<TDerived>::value, "TDerived cannot be abstract");
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
}
template<typename Serializer, typename TBase>
void serialize(Serializer& ser, TBase& obj) const {
//get derived key
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)};
auto it = _baseToDerivedMap.find(key);
assert(it != _baseToDerivedMap.end());
//convert derived hash to derived index, to make it work in cross-platform environment
auto& vec = _baseToDerivedArray.find(key.baseHash)->second;
auto derivedIndex = static_cast<size_t>(std::distance(vec.begin(), std::find(vec.begin(), vec.end(),
key.derivedHash)));
details::writeSize(ser.adapter(), derivedIndex);
//serialize
it->second->process(&ser, &obj);
}
template<typename Deserializer, typename TBase, typename TCreateFnc, typename TDestroyFnc>
void deserialize(Deserializer& des, TBase* obj,
TCreateFnc createFnc, TDestroyFnc destroyFnc) const {
size_t derivedIndex{};
details::readSize(des.adapter(), derivedIndex, 0, std::false_type{});
auto baseToDerivedVecIt = _baseToDerivedArray.find(RTTI::template get<TBase>());
//base class is known at compile time, so we can assert on this one
assert(baseToDerivedVecIt != _baseToDerivedArray.end());
if (baseToDerivedVecIt->second.size() > derivedIndex) {
//convert derived index to derived hash, to make it work in cross-platform environment
auto derivedHash = baseToDerivedVecIt->second[derivedIndex];
auto& handler = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), derivedHash})->second;
//if object is null or different type, create new and assign it
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
if (obj) {
destroyFnc(getPolymorphicHandler(*obj));
}
obj = createFnc(handler);
}
handler->process(&des, obj);
} else
des.adapter().error(ReaderError::InvalidPointer);
}
template<typename TBase>
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(TBase& obj) const {
auto deleteHandlerIt = _baseToDerivedMap.find(
BaseToDerivedKey{RTTI::template get<TBase>(), RTTI::template get<TBase>(obj)});
assert(deleteHandlerIt != _baseToDerivedMap.end());
return deleteHandlerIt->second;
}
};
}
}
#endif //BITSERY_EXT_POLYMORPHISM_UTILS_H

View File

@@ -0,0 +1,65 @@
//MIT License
//
//Copyright (c) 2018 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_RTTI_UTILS_H
#define BITSERY_RTTI_UTILS_H
#include <typeinfo>
#include <type_traits>
#include <cstddef>
namespace bitsery {
namespace ext {
struct StandardRTTI {
// static_assert(!std::is_pointer<TBase>::value &&
// !std::is_const<TBase>::value &&
// !std::is_volatile<TBase>::value, "");
template<typename TBase>
static size_t get(TBase& obj) {
return typeid(obj).hash_code();
}
template<typename TBase>
static constexpr size_t get() {
return typeid(TBase).hash_code();
}
template<typename TBase, typename TDerived>
static constexpr TDerived* cast(TBase* obj) {
static_assert(!std::is_pointer<TDerived>::value, "");
return dynamic_cast<TDerived*>(obj);
}
template<typename TBase>
static constexpr bool isPolymorphic() {
return std::is_polymorphic<TBase>::value;
}
};
}
}
#endif //BITSERY_RTTI_UTILS_H

View File

@@ -0,0 +1,219 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_EXT_VALUE_RANGE_H
#define BITSERY_EXT_VALUE_RANGE_H
#include "../details/serialization_common.h"
#include "../details/adapter_common.h"
#include <cassert>
namespace bitsery {
namespace ext {
//this class is used to make default RangeSpec float specialization always prefer constructor with precision
struct BitsConstraint {
explicit constexpr BitsConstraint(size_t bits) : value{bits} {}
const size_t value;
};
}
//implementation details for range functionality
namespace details {
template<typename T>
constexpr size_t getSize(T v, size_t s) {
return v > 0 ? getSize(v / 2, s + 1) : s;
}
template<typename T>
constexpr size_t calcRequiredBits(T min, T max) {
//call recursive function, because some compilers only support constexpr functions with return-only body
return getSize(max - min, 0);
}
template<typename T, typename Enable = void>
struct RangeSpec {
constexpr RangeSpec(T minValue, T maxValue)
: min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(min, max)} {
}
const T min;
const T max;
const size_t bitsRequired;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(
static_cast<typename std::underlying_type<T>::type>(min),
static_cast<typename std::underlying_type<T>::type>(max))} {
}
const T min;
const T max;
const size_t bitsRequired;
};
template<typename T>
struct RangeSpec<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue, ext::BitsConstraint bits) :
min{minValue},
max{maxValue},
bitsRequired{bits.value} {
}
constexpr RangeSpec(T minValue, T maxValue, T precision) :
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits<details::SameSizeUnsigned<T>>(
{}, static_cast<details::SameSizeUnsigned<T>>((max - min) / precision))} {
}
const T min;
const T max;
const size_t bitsRequired;
};
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
return static_cast<details::SameSizeUnsigned<T>>(v - r.min);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
details::SameSizeUnsigned<T> getRangeValue(const T &v, const RangeSpec<T> &r) {
using VT = details::SameSizeUnsigned<T>;
return static_cast<VT>(static_cast<VT>(v) - static_cast<VT>(r.min));
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
details::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 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>
void setRangeValue(T &v, const RangeSpec<T> &r) {
v += r.min;
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
void setRangeValue(T &v, const RangeSpec<T> &r) {
using VT = typename std::underlying_type<T>::type;
reinterpret_cast<VT &>(v) += static_cast<VT>(r.min);
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
void setRangeValue(T &v, const RangeSpec<T> &r) {
using UIT = details::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) / static_cast<T>(maxUint)) * (r.max - r.min);
}
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
return !(r.min > v || v > r.max);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
bool isRangeValid(const T &v, const RangeSpec<T> &r) {
using VT = typename std::underlying_type<T>::type;
return !(static_cast<VT>(r.min) > static_cast<VT>(v)
|| static_cast<VT>(v) > static_cast<VT>(r.max));
}
}
namespace ext {
template<typename TValue>
class ValueRange {
public:
template<typename ... Args>
constexpr ValueRange(const TValue& min, const TValue& max, Args &&... args)
:_range{min, max, std::forward<Args>(args)...} {}
template<typename Ser, typename T, typename Fnc>
void serialize(Ser &ser, const T &v, Fnc &&) const {
assert(details::isRangeValid(v, _range));
using BT = decltype(details::getRangeValue(v, _range));
ser.adapter().template writeBits<BT>(details::getRangeValue(v, _range), _range.bitsRequired);
}
template<typename Des, typename T, typename Fnc>
void deserialize(Des &des, T &v, Fnc &&) const {
auto& reader = des.adapter();
reader.readBits(reinterpret_cast<details::SameSizeUnsigned<T> &>(v), _range.bitsRequired);
details::setRangeValue(v, _range);
handleInvalidRange(reader, v, std::integral_constant<bool, Des::TConfig::CheckDataErrors>{});
}
constexpr size_t getRequiredBits() const {
return _range.bitsRequired;
};
private:
template <typename Reader, typename T>
void handleInvalidRange(Reader& reader, T& v, std::true_type) const {
if (!details::isRangeValid(v, _range)) {
reader.error(ReaderError::InvalidData);
v = _range.min;
}
}
template <typename Reader, typename T>
void handleInvalidRange(Reader&, T&, std::false_type) const {
}
details::RangeSpec<TValue> _range;
};
}
namespace traits {
template<typename T>
struct ExtensionTraits<ext::ValueRange<T>, T> {
using TValue = void;
static constexpr bool SupportValueOverload = false;
static constexpr bool SupportObjectOverload = true;
static constexpr bool SupportLambdaOverload = false;
};
}
}
#endif //BITSERY_EXT_VALUE_RANGE_H

View File

@@ -0,0 +1,540 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_SERIALIZER_H
#define BITSERY_SERIALIZER_H
#include "details/serialization_common.h"
#include "details/adapter_common.h"
#include <cassert>
namespace bitsery {
namespace details {
template<typename TAdapter>
class OutputAdapterBitPackingWrapper {
public:
static constexpr bool BitPackingEnabled = true;
using TConfig = typename TAdapter::TConfig;
using TValue = typename TAdapter::TValue;
OutputAdapterBitPackingWrapper(TAdapter& adapter)
: _wrapped{adapter}
{
}
~OutputAdapterBitPackingWrapper() {
align();
}
template<size_t SIZE, typename T>
void writeBytes(const T &v) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBytes<SIZE,T>(v);
} else {
using UT = typename std::make_unsigned<T>::type;
writeBitsInternal(reinterpret_cast<const UT &>(v), details::BitsSize<T>::value);
}
}
template<size_t SIZE, typename T>
void writeBuffer(const T *buf, size_t count) {
static_assert(std::is_integral<T>(), "");
static_assert(sizeof(T) == SIZE, "");
if (!_scratchBits) {
this->_wrapped.template writeBuffer<SIZE,T>(buf, count);
} else {
using UT = typename std::make_unsigned<T>::type;
//todo improve implementation
const auto end = buf + count;
for (auto it = buf; it != end; ++it)
writeBitsInternal(reinterpret_cast<const UT &>(*it), details::BitsSize<T>::value);
}
}
template<typename T>
void writeBits(const T &v, size_t bitsCount) {
static_assert(std::is_integral<T>() && std::is_unsigned<T>(), "");
assert(0 < bitsCount && bitsCount <= details::BitsSize<T>::value);
assert(v <= (bitsCount < 64
? (1ULL << bitsCount) - 1
: (1ULL << (bitsCount-1)) + ((1ULL << (bitsCount-1)) -1)));
writeBitsInternal(v, bitsCount);
}
void align() {
writeBitsInternal(UnsignedType{}, (details::BitsSize<UnsignedType>::value - _scratchBits) % 8);
}
void currentWritePos(size_t pos) {
align();
this->_wrapped.currentWritePos(pos);
}
size_t currentWritePos() const {
return this->_wrapped.currentWritePos();
}
void flush() {
align();
this->_wrapped.flush();
}
size_t writtenBytesCount() const {
return this->_wrapped.writtenBytesCount();
}
private:
TAdapter& _wrapped;
using UnsignedType = typename std::make_unsigned<typename TAdapter::TValue>::type;
using ScratchType = typename details::ScratchType<UnsignedType>::type;
static_assert(details::IsDefined<ScratchType>::value, "Underlying adapter value type is not supported");
template<typename T>
void writeBitsInternal(const T &v, size_t size) {
constexpr size_t valueSize = details::BitsSize<UnsignedType>::value;
T value = v;
size_t bitsLeft = size;
while (bitsLeft > 0) {
auto bits = (std::min)(bitsLeft, valueSize);
_scratch |= static_cast<ScratchType>( value ) << _scratchBits;
_scratchBits += bits;
if (_scratchBits >= valueSize) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType >(tmp);
_scratch >>= valueSize;
_scratchBits -= valueSize;
value = static_cast<T>(value >> valueSize);
}
bitsLeft -= bits;
}
}
//overload for TValue, for better performance
void writeBitsInternal(const UnsignedType &v, size_t size) {
if (size > 0) {
_scratch |= static_cast<ScratchType>( v ) << _scratchBits;
_scratchBits += size;
if (_scratchBits >= details::BitsSize<UnsignedType>::value) {
auto tmp = static_cast<UnsignedType>(_scratch & _MASK);
this->_wrapped.template writeBytes<sizeof(UnsignedType), UnsignedType>(tmp);
_scratch >>= details::BitsSize<UnsignedType>::value;
_scratchBits -= details::BitsSize<UnsignedType>::value;
}
}
}
const UnsignedType _MASK = (std::numeric_limits<UnsignedType>::max)();
ScratchType _scratch{};
size_t _scratchBits{};
};
}
template<typename TOutputAdapter, typename TContext = void>
class Serializer: public details::AdapterAndContextRef<TOutputAdapter, TContext> {
public:
//helper type, that always returns bit-packing enabled type, useful inside serialize function when enabling bitpacking
using BPEnabledType = Serializer<typename std::conditional<TOutputAdapter::BitPackingEnabled,
TOutputAdapter,
details::OutputAdapterBitPackingWrapper<TOutputAdapter>>::type, TContext>;
using TConfig = typename TOutputAdapter::TConfig;
using details::AdapterAndContextRef<TOutputAdapter, TContext>::AdapterAndContextRef;
/*
* object function
*/
template<typename T>
void object(const T &obj) {
details::SerializeFunction<Serializer, T>::invoke(*this, const_cast<T& >(obj));
}
template<typename T, typename Fnc>
void object(const T &obj, Fnc &&fnc) {
fnc(*this, const_cast<T& >(obj));
}
/*
* functionality, that enables simpler serialization syntax, by including additional header
*/
template <typename... TArgs>
Serializer &operator()(TArgs &&... args) {
archive(std::forward<TArgs>(args)...);
return *this;
}
/*
* value overloads
*/
template<size_t VSIZE, typename T>
void value(const T &v) {
static_assert(details::IsFundamentalType<T>::value, "Value must be integral, float or enum type.");
using TValue = typename details::IntegralFromFundamental<T>::TValue;
this->_adapter.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
}
/*
* enable bit-packing
*/
template <typename Fnc>
void enableBitPacking(Fnc&& fnc) {
procEnableBitPacking(std::forward<Fnc>(fnc),
std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{},
std::integral_constant<bool, Serializer::HasContext>{});
}
/*
* extension functions
*/
template<typename T, typename Ext, typename Fnc>
void ext(const T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.serialize(*this, obj, std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, typename Ext>
void ext(const T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, obj, [](Serializer& s, VType &v) { s.value<VSIZE>(v); });
}
template<typename T, typename Ext>
void ext(const T &obj, const Ext &extension) {
static_assert(details::IsExtensionTraitsDefined<Ext, T>::value, "Please define ExtensionTraits");
static_assert(traits::ExtensionTraits<Ext,T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename traits::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, obj, [](Serializer& s, VType &v) { s.object(v); });
}
/*
* boolValue
*/
void boolValue(bool v) {
procBoolValue(v, std::integral_constant<bool, TOutputAdapter::BitPackingEnabled>{});
}
/*
* text overloads
*/
template<size_t VSIZE, typename T>
void text(const T &str, size_t maxSize) {
static_assert(details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use text(const T&) overload without `maxSize` for static container");
procText<VSIZE>(str, maxSize);
}
template<size_t VSIZE, typename T>
void text(const T &str) {
static_assert(details::IsTextTraitsDefined<T>::value,
"Please define TextTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use text(const T&, size_t) overload with `maxSize` for dynamic containers");
procText<VSIZE>(str, traits::ContainerTraits<T>::size(str));
}
/*
* container overloads
*/
//dynamic size containers
template<typename T, typename Fnc>
void container(const T &obj, size_t maxSize, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&, Fnc) overload without `maxSize` for static containers");
auto size = traits::ContainerTraits<T>::size(obj);
assert(size <= maxSize);
details::writeSize(this->_adapter, size);
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(const T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&) overload without `maxSize` for static containers");
static_assert(VSIZE > 0, "");
auto size = traits::ContainerTraits<T>::size(obj);
assert(size <= maxSize);
details::writeSize(this->_adapter, size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(const T &obj, size_t maxSize) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(traits::ContainerTraits<T>::isResizable,
"use container(const T&) overload without `maxSize` for static containers");
auto size = traits::ContainerTraits<T>::size(obj);
assert(size <= maxSize);
details::writeSize(this->_adapter, size);
procContainer(std::begin(obj), std::end(obj));
}
//fixed size containers
template<typename T, typename Fnc, typename std::enable_if<!std::is_integral<Fnc>::value>::type * = nullptr>
void container(const T &obj, Fnc &&fnc) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t, Fnc) overload with `maxSize` for dynamic containers");
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T>
void container(const T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, traits::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
void container(const T &obj) {
static_assert(details::IsContainerTraitsDefined<T>::value,
"Please define ContainerTraits or include from <bitsery/traits/...>");
static_assert(!traits::ContainerTraits<T>::isResizable,
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
procContainer(std::begin(obj), std::end(obj));
}
//overloads for functions with explicit type size
template<typename T>
void value1b(T &&v) { value<1>(std::forward<T>(v)); }
template<typename T>
void value2b(T &&v) { value<2>(std::forward<T>(v)); }
template<typename T>
void value4b(T &&v) { value<4>(std::forward<T>(v)); }
template<typename T>
void value8b(T &&v) { value<8>(std::forward<T>(v)); }
template<typename T, typename Ext>
void ext1b(const T &v, Ext &&extension) { ext<1, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext2b(const T &v, Ext &&extension) { ext<2, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext4b(const T &v, Ext &&extension) { ext<4, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T, typename Ext>
void ext8b(const T &v, Ext &&extension) { ext<8, T, Ext>(v, std::forward<Ext>(extension)); }
template<typename T>
void text1b(const T &str, size_t maxSize) { text<1>(str, maxSize); }
template<typename T>
void text2b(const T &str, size_t maxSize) { text<2>(str, maxSize); }
template<typename T>
void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T>
void text1b(const T &str) { text<1>(str); }
template<typename T>
void text2b(const T &str) { text<2>(str); }
template<typename T>
void text4b(const T &str) { text<4>(str); }
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
template<typename T>
void container2b(T &&obj, size_t maxSize) { container<2>(std::forward<T>(obj), maxSize); }
template<typename T>
void container4b(T &&obj, size_t maxSize) { container<4>(std::forward<T>(obj), maxSize); }
template<typename T>
void container8b(T &&obj, size_t maxSize) { container<8>(std::forward<T>(obj), maxSize); }
template<typename T>
void container1b(T &&obj) { container<1>(std::forward<T>(obj)); }
template<typename T>
void container2b(T &&obj) { container<2>(std::forward<T>(obj)); }
template<typename T>
void container4b(T &&obj) { container<4>(std::forward<T>(obj)); }
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
private:
//process value types
//false_type means that we must process all elements individually
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::false_type) {
for (; first != last; ++first)
value<VSIZE>(*first);
}
//process value types
//true_type means, that we can copy whole buffer
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::true_type) {
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last)
this->_adapter.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)),
static_cast<size_t>(std::distance(first, last)));
}
//process by calling functions
template<typename It, typename Fnc>
void procContainer(It first, It last, Fnc fnc) {
using TValue = typename std::decay<decltype(*first)>::type;
for (; first != last; ++first) {
fnc(*this, const_cast<TValue&>(*first));
}
}
//process text,
template<size_t VSIZE, typename T>
void procText(const T& str, size_t maxSize) {
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);
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
template<typename It>
void procContainer(It first, It last) {
for (; first != last; ++first)
object(*first);
}
//proc bool writing bit or byte, depending on if BitPackingEnabled or not
void procBoolValue(bool v, std::true_type) {
this->_adapter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
}
void procBoolValue(bool v, std::false_type) {
this->_adapter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
}
//enable bit-packing or do nothing if it is already enabled
template <typename Fnc, typename HasContext>
void procEnableBitPacking(const Fnc& fnc, std::true_type, HasContext) {
fnc(*this);
}
template <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::true_type) {
//create serializer using bitpacking wrapper
BPEnabledType ser{this->_context, this->_adapter};
fnc(ser);
}
template <typename Fnc>
void procEnableBitPacking(const Fnc& fnc, std::false_type, std::false_type) {
//create serializer using bitpacking wrapper
BPEnabledType ser{this->_adapter};
fnc(ser);
}
//these are dummy functions for extensions that have TValue = void
void object(const details::DummyType&) {
}
template <size_t VSIZE>
void value(const details::DummyType&) {
}
template<typename T, typename ... TArgs>
void archive(T &&head, TArgs &&... tail) {
//serialize object
details::BriefSyntaxFunction<Serializer, T>::invoke(*this, std::forward<T>(head));
//expand other elements
archive(std::forward<TArgs>(tail)...);
}
//dummy function, that stops archive variadic arguments expansion
void archive() {
}
};
//helper function that set ups all the basic steps and after serialziation returns serialized bytes count
template <typename OutputAdapter, typename T>
size_t quickSerialization(OutputAdapter adapter, const T& value) {
Serializer<OutputAdapter> ser{std::move(adapter)};
ser.object(value);
ser.adapter().flush();
return ser.adapter().writtenBytesCount();
}
template <typename Context, typename OutputAdapter, typename T>
size_t quickSerialization(Context& ctx, OutputAdapter adapter, const T& value) {
Serializer<OutputAdapter, Context> ser{ctx, std::move(adapter)};
ser.object(value);
ser.adapter().flush();
return ser.adapter().writtenBytesCount();
}
}
#endif //BITSERY_SERIALIZER_H

View File

@@ -0,0 +1,44 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_ARRAY_H
#define BITSERY_TRAITS_STD_ARRAY_H
#include "core/std_defaults.h"
#include <array>
namespace bitsery {
namespace traits {
template<typename T, size_t N>
struct ContainerTraits<std::array<T, N>>
:public StdContainer<std::array<T, N>, false, true> {};
template<typename T, size_t N>
struct BufferAdapterTraits<std::array<T, N>>
:public StdContainerForBufferAdapter<std::array<T, N>> {};
}
}
#endif //BITSERY_TYPE_TRAITS_STD_ARRAY_H

View File

@@ -0,0 +1,103 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#define BITSERY_TRAITS_CORE_STD_DEFAULTS_H
#include "traits.h"
#include "../../details/serialization_common.h"
namespace bitsery {
namespace traits {
/*
* these are helper types, to easier write specializations for std types
*/
template<typename T, bool Resizable, bool Contiguous>
struct StdContainer {
using TValue = typename T::value_type;
static constexpr bool isResizable = Resizable;
static constexpr bool isContiguous = Contiguous;
static size_t size(const T& container) {
return container.size();
}
};
//specialization for resizable
template<typename T, bool Contiguous>
struct StdContainer<T, true, Contiguous> {
using TValue = typename T::value_type;
static constexpr bool isResizable = true;
static constexpr bool isContiguous = Contiguous;
static size_t size(const T& container) {
return container.size();
}
static void resize(T& container, size_t size) {
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);
}
static void resizeImpl(T& container, size_t newSize, std::false_type) {
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_back(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
container.erase(std::next(std::begin(container), static_cast<diff_t>(newSize)), std::end(container));
}
}
};
template <typename T, bool Resizable = ContainerTraits<T>::isResizable>
struct StdContainerForBufferAdapter {
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
};
//specialization for resizable buffers
template <typename T>
struct StdContainerForBufferAdapter<T, true> {
static void increaseBufferSize(T& container) {
//since we're writing to buffer use different resize strategy than default implementation
//when small size grow faster, to avoid thouse 2/4/8/16... byte allocations
auto newSize = static_cast<size_t>(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()));
}
using TIterator = typename T::iterator;
using TConstIterator = typename T::const_iterator;
using TValue = typename ContainerTraits<T>::TValue;
};
}
}
#endif //BITSERY_TRAITS_CORE_STD_DEFAULTS_H

View File

@@ -0,0 +1,187 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_CORE_TRAITS_H
#define BITSERY_TRAITS_CORE_TRAITS_H
#include <type_traits>
#include "../../details/not_defined_type.h"
namespace bitsery {
namespace traits {
/*
* core library traits, used to extend library for custom types
*/
//traits for extension
template<typename Extension, typename T>
struct ExtensionTraits {
//this type is used, when using extesion without custom lambda
// eg.: extension4b>(obj, myextension{}) will call s.value4b(obj) for TValue
// or extesion(obj, myextension{}) will call s.object(obj) for TValue
//when this is void, it will compile, but value and object overloads will do nothing.
using TValue = details::NotDefinedType;
//does extension support ext<N>(...) syntax, by calling value<N> with TValue
static constexpr bool SupportValueOverload = false;
//does extension support ext(...) syntax, by calling object with TValue
static constexpr bool SupportObjectOverload = false;
//does extension support ext(..., lambda)
static constexpr bool SupportLambdaOverload = false;
};
//primary traits for containers
template<typename T>
struct ContainerTraits {
using TValue = details::NotDefinedType;
static constexpr bool isResizable = false;
//contiguous arrays has oppurtunity to memcpy whole buffer directly when using funtamental types
//contiguous doesn't nesessary equal to random access iterator.
//contiguous hopefully will be available in c++20
static constexpr bool isContiguous = false;
//resize function, called only if container is resizable
static void resize(T& , size_t ) {
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
}
//get container size
static size_t size(const T& ) {
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
return 0u;
}
};
//specialization for C style array
template<typename T, size_t N>
struct ContainerTraits<T[N]> {
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T (&)[N]) {
return N;
}
};
//specialization for initializer list.
//only serializer can use it
template<typename T>
struct ContainerTraits<std::initializer_list<T>> {
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const std::initializer_list<T>& container) {
return container.size();
}
};
//specialization for pointer type buffer
//only deserializer can use it
template <typename T>
struct ContainerTraits<const T*> {
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T* ) {
static_assert(std::is_void<T>::value, "cannot get size for container of type T*");
return 0u;
}
};
template <typename T>
struct ContainerTraits<T*> {
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T* ) {
static_assert(std::is_void<T>::value, "cannot get size for container of type T*");
return 0u;
}
};
//traits for text, default adds null-terminated character at the end
template<typename T>
struct TextTraits {
using TValue = details::NotDefinedType;
//if container is not null-terminated by default, add NUL at the end
static constexpr bool addNUL = true;
//get length of null terminated container
static size_t length(const T& ) {
static_assert(std::is_void<T>::value,
"Define TextTraits or include from <bitsery/traits/...> to use as text");
return 0u;
}
};
//traits only for buffer adapters
template <typename T>
struct BufferAdapterTraits {
//this function is only applies to resizable containers
//this function is only used by Writer, when writing data to buffer,
//it is called only current buffer size is not enough to write.
//it is used to dramaticaly improve performance by updating buffer directly
//instead of using back_insert_iterator to append each byte to buffer.
static void increaseBufferSize(T& ) {
static_assert(std::is_void<T>::value,
"Define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer adapter container");
}
using TIterator = details::NotDefinedType;
using TConstIterator = details::NotDefinedType;
using TValue = typename ContainerTraits<T>::TValue;
};
//specialization for c-style buffer
template <typename T, size_t N>
struct BufferAdapterTraits<T[N]> {
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;
};
//specialization for pointer type buffer
template <typename T>
struct BufferAdapterTraits<const T*> {
using TIterator = const T*;
using TConstIterator = const T*;
using TValue = T;
};
template <typename T>
struct BufferAdapterTraits<T*> {
using TIterator = T*;
using TConstIterator = const T*;
using TValue = T;
};
}
}
#endif //BITSERY_TRAITS_CORE_TRAITS_H

View File

@@ -0,0 +1,42 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_DEQUE_H
#define BITSERY_TRAITS_STD_DEQUE_H
#include "core/std_defaults.h"
#include <deque>
namespace bitsery {
namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::deque<T, Allocator>>
: public StdContainer<std::deque<T, Allocator>, true, false> {};
}
}
#endif //BITSERY_TRAITS_STD_DEQUE_H

View File

@@ -0,0 +1,69 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_FORWARD_LIST_H
#define BITSERY_TRAITS_STD_FORWARD_LIST_H
#include "core/traits.h"
#include "../details/serialization_common.h"
#include <forward_list>
namespace bitsery {
namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::forward_list<T, Allocator>> {
using TValue = T;
static constexpr bool isResizable = true;
static constexpr bool isContiguous = false;
static size_t size(const std::forward_list<T, Allocator>& container) {
return static_cast<size_t>(std::distance(container.begin(), container.end()));
}
static void resize(std::forward_list<T, Allocator>& container, size_t size) {
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);
}
static void resizeImpl(std::forward_list<T, Allocator>& container, size_t newSize, std::false_type) {
const auto oldSize = size(container);
for (auto it = oldSize; it < newSize; ++it) {
container.push_front(::bitsery::Access::create<TValue>());
}
if (oldSize > newSize) {
//erase_after must have atleast one element to work
if (newSize > 0)
container.erase_after(std::next(std::begin(container), static_cast<diff_t>(newSize-1)));
else
container.clear();
}
}
};
}
}
#endif //BITSERY_TRAITS_STD_FORWARD_LIST_H

View File

@@ -0,0 +1,42 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_LIST_H
#define BITSERY_TRAITS_STD_LIST_H
#include "core/std_defaults.h"
#include <list>
namespace bitsery {
namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::list<T, Allocator>>
: public StdContainer<std::list<T, Allocator>, true, false> {};
}
}
#endif //BITSERY_TRAITS_STD_LIST_H

View File

@@ -0,0 +1,71 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_STRING_H
#define BITSERY_TRAITS_STD_STRING_H
#include "core/std_defaults.h"
#include <string>
namespace bitsery {
namespace traits {
// specialization for string, because string is already included for std::char_traits
template<typename CharT, typename Traits, typename Allocator>
struct ContainerTraits<std::basic_string<CharT, Traits, Allocator>>
:public StdContainer<std::basic_string<CharT, Traits, Allocator>, true, true> {};
template <typename CharT, typename Traits, typename Allocator>
struct TextTraits<std::basic_string<CharT, Traits, Allocator>> {
using TValue = typename ContainerTraits<std::basic_string<CharT, Traits, Allocator>>::TValue;
//string is automatically null-terminated
static constexpr bool addNUL = false;
//is is not 100% accurate, but for performance reasons assume that string stores text, not binary data
static size_t length(const std::basic_string<CharT, Traits, Allocator>& str) {
return str.size();
}
};
//specialization for c-array
template <typename T, size_t N>
struct TextTraits<T[N]> {
using TValue = T;
static constexpr bool addNUL = true;
static size_t length(const T (&container)[N]) {
return std::char_traits<T>::length(container);
}
};
template<typename CharT, typename Traits, typename Allocator>
struct BufferAdapterTraits<std::basic_string<CharT, Traits, Allocator>>
:public StdContainerForBufferAdapter<std::basic_string<CharT, Traits, Allocator>> {};
}
}
#endif //BITSERY_TRAITS_VECTOR_H

View File

@@ -0,0 +1,50 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_TRAITS_STD_VECTOR_H
#define BITSERY_TRAITS_STD_VECTOR_H
#include "core/std_defaults.h"
#include <vector>
namespace bitsery {
namespace traits {
template<typename T, typename Allocator>
struct ContainerTraits<std::vector<T, Allocator>>
:public StdContainer<std::vector<T, Allocator>, true, true> {};
//bool vector is not contiguous, do not copy it directly to buffer
template<typename Allocator>
struct ContainerTraits<std::vector<bool, Allocator>>
:public StdContainer<std::vector<bool, Allocator>, true, false> {};
template<typename T, typename Allocator>
struct BufferAdapterTraits<std::vector<T, Allocator>>
:public StdContainerForBufferAdapter<std::vector<T, Allocator>> {};
}
}
#endif //BITSERY_TRAITS_STD_VECTOR_H

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`.

Some files were not shown because too many files have changed in this diff Show More