mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-18 21:29:05 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d24dfe14f5 | ||
|
|
ee68261124 | ||
|
|
d22b7c1527 | ||
|
|
8d6ffc9873 | ||
|
|
0e76e0608c | ||
|
|
501d60f67d | ||
|
|
04afd16fbd | ||
|
|
9621db1cd7 | ||
|
|
c555088aa3 | ||
|
|
c9619e3e3d | ||
|
|
01d56e00b8 | ||
|
|
04526ff0f4 | ||
|
|
b7d159bbfc |
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,3 +1,28 @@
|
||||
# [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.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(bitsery
|
||||
LANGUAGES CXX
|
||||
VERSION 5.0.0)
|
||||
VERSION 5.0.3)
|
||||
|
||||
#======== build options ===================================
|
||||
option(BITSERY_BUILD_EXAMPLES "Build examples" OFF)
|
||||
|
||||
32
README.md
32
README.md
@@ -18,28 +18,27 @@ All cross-platform requirements are enforced at compile time, so serialized data
|
||||
* Configurable runtime error checking on deserialization.
|
||||
* Can read/write from any source: stream (file, network stream. etc... ), or buffer (vector, c-array, etc...).
|
||||
* Don't pay for what you don't use! - customize your serialization via **extensions**. Some notable *extensions* allow:
|
||||
* forward/backward compatibility for your types.
|
||||
* smart and raw pointers with customizable runtime polymorphism support.
|
||||
* fine-grained bit-level serialization control.
|
||||
* forward/backward compatibility for your types.
|
||||
* smart and raw pointers with allocators support and customizable runtime polymorphism.
|
||||
* Easily extendable for any type.
|
||||
* Allows brief or/and verbose syntax for better serialization control.
|
||||
* Allows brief (similar to [cereal](https://uscilab.github.io/cereal/)) or/and verbose syntax for better serialization control.
|
||||
* Configurable endianness support.
|
||||
* No macros.
|
||||
|
||||
## Why to use bitsery
|
||||
## Why use bitsery
|
||||
|
||||
Look at the numbers and features list, and decide yourself.
|
||||
|
||||
| | data size | serialize | deserialize |
|
||||
|------------------|-----------|-----------|-------------|
|
||||
| bitsery | 6913B | 1252ms | 1170ms |
|
||||
| bitsery_compress | 4213B | 1445ms | 1325ms |
|
||||
| boost | 11037B | 9952ms | 8767ms |
|
||||
| cereal | 10413B | 6497ms | 5470ms |
|
||||
| flatbuffers | 14924B | 6762ms | 2173ms |
|
||||
| yas | 10463B | 1352ms | 1109ms |
|
||||
| yas_compress | 7315B | 1673ms | 1598ms |
|
||||
|
||||
| library | data size | serialize | deserialize |
|
||||
| ---------------- | --------- | --------- | ----------- |
|
||||
| bitsery | 6913B | 959ms | 927ms |
|
||||
| bitsery_compress | 4213B | 1282ms | 1115ms |
|
||||
| boost | 11037B | 9826ms | 8313ms |
|
||||
| cereal | 10413B | 6324ms | 5698ms |
|
||||
| flatbuffers | 14924B | 5129ms | 2142ms |
|
||||
| protobuf | 10018B | 11966ms | 13919ms |
|
||||
| yas | 10463B | 1908ms | 1217ms |
|
||||
|
||||
*benchmarked on Ubuntu with GCC 8.3.0, more details can be found [here](https://github.com/fraillt/cpp_serializers_benchmark.git)*
|
||||
|
||||
@@ -103,9 +102,12 @@ Works with C++11 compiler, no additional dependencies, include `<bitsery/bitsery
|
||||
|
||||
This library was tested on
|
||||
* Windows: Visual Studio 2015, MinGW (GCC 5.2)
|
||||
* Linux: GCC 5.4, GCC 6.2, Clang 3.9
|
||||
* Linux: GCC 5.4, Clang 3.9
|
||||
* OS X Mavericks: AppleClang 8
|
||||
|
||||
There is a patch that allows using bitsery with non-fully compatible C++11 compilers.
|
||||
* CentOS 7 with gcc 4.8.2.
|
||||
|
||||
## License
|
||||
|
||||
**bitsery** is licensed under the [MIT license](LICENSE).
|
||||
|
||||
@@ -6,7 +6,7 @@ Library design:
|
||||
* `valueNb instead of value`
|
||||
* `brief syntax`
|
||||
* `serializer/deserializer functions overloads`
|
||||
* `extending library functionality`
|
||||
* [extending library functionality](design/extensions.md)
|
||||
* `errors handling`
|
||||
* `forward/backward compatibility via Growable extension`
|
||||
* [pointers](design/pointers.md)
|
||||
@@ -64,8 +64,10 @@ Output adapters (buffer and stream) functions:
|
||||
* `writeBytes`
|
||||
* `writeBuffer`
|
||||
* `flush`
|
||||
* `currentyWritePos (get/set)` (buffer adapter only)
|
||||
* `writtenBytesCount` (buffer adapter only)
|
||||
* `currentyWritePos (get/set)` (buffer adapter only) gets/sets write position in buffer, it can jump past the buffer end, in this case buffer will be resized.
|
||||
This function doesn't write any bytes.
|
||||
* `writtenBytesCount` (buffer adapter only) this doesn't necessary mean how many bytes are written, but rather how many bytes in the buffer was "affected" during serialization.
|
||||
E.g. if `currentyWritePos` (set) jumps from 0 to 100, and then 4 bytes are written, `writtenBytesCount` return 104, it also returns 104 if you jump in somewhere in the middle.
|
||||
|
||||
|
||||
Tips and tricks:
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
## Motivation
|
||||
|
||||
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++.
|
||||
Inspiration to create **bitsery** came mainly because there aren't any good alternatives for C++ that meets my requirements.
|
||||
|
||||
I wanted serializer that is easy to use like [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is save to use with untrusted (malicious) data, and most importantly is fast and has small binary footprint.
|
||||
I wanted serializer that is easy to use as [cereal](http://uscilab.github.io/cereal/), is cross-platform compatible, and has support for forward/backward compatibility like [flatbuffers](https://google.github.io/flatbuffers/), is safe to use with untrusted (malicious) data, and most importantly is fast and has a small binary footprint.
|
||||
|
||||
Furthermore I wanted full serialization control and ability to work on bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce size by large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
|
||||
Furthermore, I wanted full serialization control and the ability to work on a bit level, so I can further reduce data size. For example, serializing container of [quaternions](https://en.wikipedia.org/wiki/Quaternion) I can reduce the size by a large amount. *Size of orientation quaternion can be reduced from 128bits (4floats) down to 29bits using "smallest three" technique and still retaining decent precision*.
|
||||
|
||||
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also adds additional library complexity.
|
||||
Most well-known serialization libraries sacrifice memory and speed efficiency by supporting multiple data formats (binary, json, xml) and multiple languages (C++, C#, Javascript, etc..), these features also add additional library complexity.
|
||||
|
||||
## A word about JSON
|
||||
|
||||
Often times people use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format.
|
||||
Often people use C++ because they want speed and memory efficiency, and JSON is not on the list of efficient serialization format.
|
||||
Although JSON is very readable and very convenient when used together with dynamically typed languages such as JavaScript.
|
||||
When serializing data from statically typed languages, however, JSON not only has the obvious drawback of runtime inefficiency, but also forces you to write more code to access data (counterintuitively) due to its dynamic-typing serialization system.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -28,57 +28,27 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
|
||||
template<typename Buffer>
|
||||
class BufferIterators {
|
||||
static constexpr bool isConstBuffer = std::is_const<Buffer>::value;
|
||||
using BuffNonConst = typename std::remove_const<Buffer>::type;
|
||||
public:
|
||||
BufferIterators(const BufferIterators&) = delete;
|
||||
BufferIterators& operator=(const BufferIterators&) = delete;
|
||||
BufferIterators(BufferIterators&&) = default;
|
||||
BufferIterators& operator=(BufferIterators&&) = default;
|
||||
virtual ~BufferIterators() = default;
|
||||
protected:
|
||||
|
||||
using TIterator = typename std::conditional<isConstBuffer,
|
||||
typename traits::BufferAdapterTraits<BuffNonConst>::TConstIterator,
|
||||
typename traits::BufferAdapterTraits<BuffNonConst>::TIterator>::type;
|
||||
static_assert(details::IsDefined<TIterator>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
BufferIterators(TIterator begin, TIterator end)
|
||||
: beginIt{begin},
|
||||
posIt{begin},
|
||||
endIt{end} {
|
||||
}
|
||||
|
||||
TIterator beginIt;
|
||||
TIterator posIt;
|
||||
TIterator endIt;
|
||||
};
|
||||
|
||||
template<typename Buffer, typename Config = DefaultConfig>
|
||||
class InputBufferAdapter: public BufferIterators<Buffer>,
|
||||
public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
|
||||
class InputBufferAdapter: public details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>> {
|
||||
public:
|
||||
friend details::InputAdapterBaseCRTP<InputBufferAdapter<Buffer,Config>>;
|
||||
using TConfig = Config;
|
||||
using TIterator = typename BufferIterators<Buffer>::TIterator;
|
||||
using TIterator = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TConstIterator;
|
||||
using TValue = typename traits::BufferAdapterTraits<typename std::remove_const<Buffer>::type>::TValue;
|
||||
static_assert(details::IsDefined<TValue>::value,
|
||||
"Please define BufferAdapterTraits or include from <bitsery/traits/...>");
|
||||
static_assert(traits::ContainerTraits<typename std::remove_const<Buffer>::type>::isContiguous,
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
InputBufferAdapter(TIterator begin, TIterator endIt)
|
||||
: BufferIterators<Buffer>(begin, endIt),
|
||||
_endReadPos{endIt} {
|
||||
InputBufferAdapter(TIterator beginIt, size_t size)
|
||||
: _beginIt{beginIt},
|
||||
_currOffset{0},
|
||||
_endReadOffset{size},
|
||||
_bufferSize{size} {
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
InputBufferAdapter(TIterator begin, size_t size)
|
||||
: BufferIterators<Buffer>(begin, std::next(begin, size)),
|
||||
_endReadPos{std::next(begin, size)} {
|
||||
InputBufferAdapter(TIterator beginIt, TIterator endIt)
|
||||
:InputBufferAdapter(beginIt, std::distance(beginIt, endIt)) {
|
||||
}
|
||||
|
||||
InputBufferAdapter(const InputBufferAdapter&) = delete;
|
||||
@@ -92,18 +62,17 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
size_t currentReadPos() const {
|
||||
return static_cast<size_t>(std::distance(this->beginIt, this->posIt));
|
||||
return currentReadPosChecked(std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void currentReadEndPos(size_t pos) {
|
||||
// assert that CheckAdapterErrors is enabled, otherwise it will simply will not work even if data and buffer is not corrupted
|
||||
static_assert(Config::CheckAdapterErrors, "Please enable CheckAdapterErrors to use this functionality.");
|
||||
const auto buffSize = static_cast<size_t>(std::distance(this->beginIt, this->endIt));
|
||||
if (buffSize >= pos) {
|
||||
if (_bufferSize >= pos && error() == ReaderError::NoError) {
|
||||
_overflowOnReadEndPos = pos == 0;
|
||||
if (pos == 0)
|
||||
pos = buffSize;
|
||||
_endReadPos = std::next(this->beginIt, pos);
|
||||
pos = _bufferSize;
|
||||
_endReadOffset = pos;
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
@@ -112,44 +81,73 @@ namespace bitsery {
|
||||
size_t currentReadEndPos() const {
|
||||
if (_overflowOnReadEndPos)
|
||||
return 0;
|
||||
return static_cast<size_t>(std::distance(this->beginIt, _endReadPos));
|
||||
return _endReadOffset;
|
||||
}
|
||||
|
||||
ReaderError error() const {
|
||||
return _err;
|
||||
return _currOffset <= _endReadOffset
|
||||
? ReaderError::NoError
|
||||
: static_cast<ReaderError>(_currOffset - _endReadOffset);
|
||||
}
|
||||
|
||||
void error(ReaderError error) {
|
||||
if (_err == ReaderError::NoError) {
|
||||
_err = error;
|
||||
_endReadPos = this->endIt;
|
||||
this->beginIt = this->endIt;
|
||||
this->posIt = this->endIt;
|
||||
if (_currOffset <= _endReadOffset) {
|
||||
_endReadOffset = 0;
|
||||
_bufferSize = 0;
|
||||
_currOffset = static_cast<size_t>(error);
|
||||
}
|
||||
}
|
||||
|
||||
bool isCompletedSuccessfully() const {
|
||||
return this->posIt == this->endIt && _err == ReaderError::NoError;
|
||||
return _currOffset == _bufferSize;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void readChecked(TValue *data, size_t size, std::false_type) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
assert(std::distance(this->posIt, this->endIt) >= 0);
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
template <size_t SIZE>
|
||||
void readInternalValue(TValue *data) {
|
||||
readInternalValueChecked<SIZE>(data, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void readChecked(TValue *data, size_t size, std::true_type) {
|
||||
//for optimization
|
||||
auto tmp = this->posIt;
|
||||
this->posIt += size;
|
||||
if (std::distance(this->posIt, _endReadPos) >= 0) {
|
||||
std::memcpy(data, std::addressof(*tmp), size);
|
||||
void readInternalBuffer(TValue *data, size_t size) {
|
||||
readInternalBufferChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
template <size_t SIZE>
|
||||
void readInternalValueChecked(TValue *data, std::false_type) {
|
||||
const auto newOffset = _currOffset + SIZE;
|
||||
assert(newOffset <= _endReadOffset);
|
||||
std::copy_n(_beginIt + _currOffset, SIZE, data);
|
||||
_currOffset = newOffset;
|
||||
}
|
||||
|
||||
template <size_t SIZE>
|
||||
void readInternalValueChecked(TValue *data, std::true_type) {
|
||||
const auto newOffset = _currOffset + SIZE;
|
||||
if (newOffset <= _endReadOffset) {
|
||||
std::copy_n(_beginIt + _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 auto newOffset = _currOffset + size;
|
||||
assert(newOffset <= _endReadOffset);
|
||||
std::copy_n(_beginIt + _currOffset, size, data);
|
||||
_currOffset = newOffset;
|
||||
}
|
||||
|
||||
void readInternalBufferChecked(TValue *data, size_t size, std::true_type) {
|
||||
const auto newOffset = _currOffset + size;
|
||||
if (newOffset <= _endReadOffset) {
|
||||
std::copy_n(_beginIt + _currOffset, size, data);
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
this->posIt -= size;
|
||||
//set everything to zeros
|
||||
std::memset(data, 0, size);
|
||||
if (_overflowOnReadEndPos)
|
||||
@@ -157,24 +155,31 @@ namespace bitsery {
|
||||
}
|
||||
}
|
||||
|
||||
void readInternal(TValue *data, size_t size) {
|
||||
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void currentReadPosChecked(size_t pos, std::true_type) {
|
||||
if (static_cast<size_t>(std::distance(this->beginIt, this->endIt)) >= pos) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
if (_bufferSize >= pos && error() == ReaderError::NoError) {
|
||||
_currOffset = pos;
|
||||
} else {
|
||||
error(ReaderError::DataOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
void currentReadPosChecked(size_t pos, std::false_type) {
|
||||
this->posIt = std::next(this->beginIt, pos);
|
||||
_currOffset = pos;
|
||||
}
|
||||
|
||||
TIterator _endReadPos;
|
||||
ReaderError _err = ReaderError::NoError;
|
||||
size_t currentReadPosChecked(std::true_type) const {
|
||||
return error() == ReaderError::NoError ? _currOffset : 0;
|
||||
}
|
||||
|
||||
size_t currentReadPosChecked(std::false_type) const {
|
||||
return _currOffset;
|
||||
}
|
||||
|
||||
|
||||
TIterator _beginIt;
|
||||
size_t _currOffset;
|
||||
size_t _endReadOffset;
|
||||
size_t _bufferSize;
|
||||
bool _overflowOnReadEndPos = true;
|
||||
};
|
||||
|
||||
@@ -192,8 +197,8 @@ namespace bitsery {
|
||||
"BufferAdapter only works with contiguous containers");
|
||||
|
||||
OutputBufferAdapter(Buffer &buffer)
|
||||
: _buffer{std::addressof(buffer)} {
|
||||
|
||||
: _buffer{std::addressof(buffer)},
|
||||
_beginIt{std::begin(buffer)} {
|
||||
init(TResizable{});
|
||||
}
|
||||
|
||||
@@ -203,8 +208,7 @@ namespace bitsery {
|
||||
OutputBufferAdapter& operator = (OutputBufferAdapter&&) = default;
|
||||
|
||||
void currentWritePos(size_t pos) {
|
||||
const auto currPos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
const auto maxPos = currPos > pos ? currPos : pos;
|
||||
const auto maxPos = _currOffset > pos ? _currOffset : pos;
|
||||
if (maxPos > _biggestCurrentPos) {
|
||||
_biggestCurrentPos = maxPos;
|
||||
}
|
||||
@@ -212,7 +216,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
size_t currentWritePos() const {
|
||||
return static_cast<size_t> (std::distance(std::begin(*_buffer), _outIt));
|
||||
return _currOffset;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
@@ -220,21 +224,26 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
size_t writtenBytesCount() const {
|
||||
const auto pos =static_cast<size_t>(std::distance(std::begin(*_buffer), _outIt));
|
||||
return pos > _biggestCurrentPos ? pos : _biggestCurrentPos;
|
||||
return _currOffset > _biggestCurrentPos ? _currOffset : _biggestCurrentPos;
|
||||
}
|
||||
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<Buffer>::isResizable>;
|
||||
|
||||
void writeInternal(const TValue *data, size_t size) {
|
||||
writeInternalImpl(data, size, TResizable{});
|
||||
template <size_t SIZE>
|
||||
void writeInternalValue(const TValue *data) {
|
||||
writeInternalValueImpl<SIZE>(data, TResizable{});
|
||||
}
|
||||
|
||||
void writeInternalBuffer(const TValue *data, size_t size) {
|
||||
writeInternalBufferImpl(data, size, TResizable{});
|
||||
}
|
||||
|
||||
Buffer* _buffer;
|
||||
TIterator _outIt{};
|
||||
TIterator _end{};
|
||||
size_t _biggestCurrentPos{};
|
||||
TIterator _beginIt;
|
||||
size_t _currOffset{0};
|
||||
size_t _bufferSize{0};
|
||||
size_t _biggestCurrentPos{0};
|
||||
|
||||
/*
|
||||
* resizable buffer
|
||||
@@ -245,47 +254,40 @@ namespace bitsery {
|
||||
if (traits::ContainerTraits<Buffer>::size(*_buffer) == 0u) {
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
}
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::begin(*_buffer);
|
||||
updateIteratorAndSize();
|
||||
}
|
||||
|
||||
void writeInternalImpl(const TValue *data, const size_t size, std::true_type) {
|
||||
//optimization
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<TIterator>::difference_type;
|
||||
if (std::distance(_outIt , _end) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
#else
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt, _end) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
#endif
|
||||
template <size_t SIZE>
|
||||
void writeInternalValueImpl(const TValue *data, std::true_type) {
|
||||
const auto newOffset = _currOffset + SIZE;
|
||||
if (newOffset <= _bufferSize) {
|
||||
std::copy_n(data, SIZE, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
|
||||
#else
|
||||
_outIt -= size;
|
||||
#endif
|
||||
//get current position before invalidating iterators
|
||||
const auto pos = std::distance(std::begin(*_buffer), _outIt);
|
||||
//increase container size
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
//restore iterators
|
||||
_end = std::end(*_buffer);
|
||||
_outIt = std::next(std::begin(*_buffer), pos);
|
||||
updateIteratorAndSize();
|
||||
writeInternalValueImpl<SIZE>(data, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
writeInternalImpl(data, size, std::true_type{});
|
||||
void writeInternalBufferImpl(const TValue *data, const size_t size, std::true_type) {
|
||||
const auto newOffset = _currOffset + size;
|
||||
if (newOffset <= _bufferSize) {
|
||||
std::copy_n(data, size, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
updateIteratorAndSize();
|
||||
writeInternalBufferImpl(data, size, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
void setCurrentWritePos(size_t pos, std::true_type) {
|
||||
const auto begin = std::begin(*_buffer);
|
||||
if (static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos) {
|
||||
_outIt = std::next(begin, pos);
|
||||
if (pos <= _bufferSize) {
|
||||
_currOffset = pos;
|
||||
} else {
|
||||
traits::BufferAdapterTraits<Buffer>::increaseBufferSize(*_buffer);
|
||||
updateIteratorAndSize();
|
||||
setCurrentWritePos(pos, std::true_type{});
|
||||
}
|
||||
}
|
||||
@@ -294,22 +296,32 @@ namespace bitsery {
|
||||
* non resizable buffer
|
||||
*/
|
||||
void init(std::false_type) {
|
||||
_outIt = std::begin(*_buffer);
|
||||
_end = std::end(*_buffer);
|
||||
updateIteratorAndSize();
|
||||
}
|
||||
|
||||
void writeInternalImpl(const TValue *data, size_t size, std::false_type) {
|
||||
//optimization
|
||||
auto tmp = _outIt;
|
||||
_outIt += size;
|
||||
assert(std::distance(_outIt, _end) >= 0);
|
||||
memcpy(std::addressof(*tmp), data, size);
|
||||
template <size_t SIZE>
|
||||
void writeInternalValueImpl(const TValue *data, std::false_type) {
|
||||
const auto newOffset = _currOffset + SIZE;
|
||||
assert(newOffset <= _bufferSize);
|
||||
std::copy_n(data, SIZE, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
}
|
||||
|
||||
void writeInternalBufferImpl(const TValue *data, size_t size, std::false_type) {
|
||||
const auto newOffset = _currOffset + size;
|
||||
assert(newOffset <= _bufferSize);
|
||||
std::copy_n(data, size, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
}
|
||||
|
||||
void setCurrentWritePos(size_t pos, std::false_type) {
|
||||
const auto begin = std::begin(*_buffer);
|
||||
assert(static_cast<size_t>(std::distance(begin, std::end(*_buffer))) >= pos);
|
||||
_outIt = std::next(begin, pos);
|
||||
assert(pos <= _bufferSize);
|
||||
_currOffset = pos;
|
||||
}
|
||||
|
||||
void updateIteratorAndSize() {
|
||||
_beginIt = std::begin(*_buffer);
|
||||
_bufferSize = traits::ContainerTraits<Buffer>::size(*_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -83,7 +83,12 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
|
||||
void readInternal(TValue* data, size_t size) {
|
||||
template <size_t SIZE>
|
||||
void readInternalValue(TValue* data) {
|
||||
readChecked(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
void readInternalBuffer(TValue* data, size_t size) {
|
||||
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
|
||||
}
|
||||
|
||||
@@ -139,8 +144,12 @@ namespace bitsery {
|
||||
|
||||
private:
|
||||
|
||||
void writeInternal(const TValue* data, size_t size) {
|
||||
//for optimization
|
||||
template <size_t SIZE>
|
||||
void writeInternalValue(const TValue* data) {
|
||||
_ios->rdbuf()->sputn( data , SIZE );
|
||||
}
|
||||
|
||||
void writeInternalBuffer(const TValue* data, size_t size) {
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
}
|
||||
|
||||
@@ -163,9 +172,12 @@ namespace bitsery {
|
||||
BasicBufferedOutputStreamAdapter(std::basic_ios<TChar, CharTraits>& ostream, size_t bufferSize = 256)
|
||||
:_ios(std::addressof(ostream)),
|
||||
_buf{},
|
||||
_outIt{}
|
||||
_beginIt{std::begin(_buf)},
|
||||
_currOffset{0}
|
||||
{
|
||||
init(bufferSize, TResizable{});
|
||||
// buffer size must be atleast 16, because writeIntervalValue expect that atleast one value fits to buffer.
|
||||
assert(_bufferSize >= 16);
|
||||
}
|
||||
|
||||
//we need to explicitly declare move logic, because after move buffer might be invalidated
|
||||
@@ -174,20 +186,19 @@ namespace bitsery {
|
||||
|
||||
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
|
||||
: _ios{rhs._ios},
|
||||
_buf{},
|
||||
_outIt{}
|
||||
_buf{std::move(rhs._buf)},
|
||||
_beginIt{std::begin(_buf)},
|
||||
_currOffset{rhs._currOffset},
|
||||
_bufferSize{rhs._bufferSize}
|
||||
{
|
||||
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
|
||||
_buf = std::move(rhs._buf);
|
||||
_outIt = std::next(std::begin(_buf), size);
|
||||
};
|
||||
|
||||
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
|
||||
_ios = rhs._ios;
|
||||
//get current written size, before move
|
||||
auto size = std::distance(std::begin(rhs._buf), rhs._outIt);
|
||||
_buf = std::move(rhs._buf);
|
||||
_outIt = std::next(std::begin(_buf), size);
|
||||
_beginIt = std::begin(_buf);
|
||||
_currOffset = rhs._currOffset;
|
||||
_bufferSize = rhs._bufferSize;
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -201,9 +212,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
void flush() {
|
||||
auto begin = std::begin(_buf);
|
||||
writeToStream(std::addressof(*begin), static_cast<size_t>(std::distance(begin, _outIt)));
|
||||
_outIt = begin;
|
||||
writeBufferToStream();
|
||||
if (auto ostream = dynamic_cast<std::basic_ostream<TChar, CharTraits>*>(_ios))
|
||||
ostream->flush();
|
||||
}
|
||||
@@ -217,44 +226,50 @@ namespace bitsery {
|
||||
private:
|
||||
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
|
||||
|
||||
void writeInternal(const TValue* data, size_t size) {
|
||||
auto tmp = _outIt;
|
||||
template <size_t SIZE>
|
||||
void writeInternalValue(const TValue* data) {
|
||||
auto newOffset = _currOffset + SIZE;
|
||||
if (newOffset > _bufferSize) {
|
||||
writeBufferToStream();
|
||||
newOffset = SIZE;
|
||||
}
|
||||
std::copy_n(data, SIZE, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && (_ITERATOR_DEBUG_LEVEL > 0)
|
||||
using TDistance = typename std::iterator_traits<BufferIt>::difference_type;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= static_cast<TDistance>(size)) {
|
||||
std::memcpy(std::addressof(*_outIt), data, size);
|
||||
_outIt += size;
|
||||
}
|
||||
#else
|
||||
_outIt += size;
|
||||
if (std::distance(_outIt , std::end(_buf)) >= 0) {
|
||||
std::memcpy(std::addressof(*tmp), data, size);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
//when buffer is full write out to stream
|
||||
_outIt = std::begin(_buf);
|
||||
writeToStream(std::addressof(*_outIt), static_cast<size_t>(std::distance(_outIt, tmp)));
|
||||
writeToStream(data, size);
|
||||
void writeInternalBuffer(const TValue* data, size_t size) {
|
||||
const auto newOffset = _currOffset + size;
|
||||
if (newOffset <= _bufferSize) {
|
||||
std::copy_n(data, size, _beginIt + _currOffset);
|
||||
_currOffset = newOffset;
|
||||
} else {
|
||||
writeBufferToStream();
|
||||
// write buffer directly to stream
|
||||
_ios->rdbuf()->sputn(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void writeToStream(const TValue* data, size_t size) {
|
||||
_ios->rdbuf()->sputn( data , size );
|
||||
void writeBufferToStream() {
|
||||
_ios->rdbuf()->sputn(std::addressof(*_beginIt), _currOffset);
|
||||
_currOffset = 0;
|
||||
}
|
||||
|
||||
void init (size_t bufferSize, std::true_type) {
|
||||
_buf.resize(bufferSize);
|
||||
_outIt = std::begin(_buf);
|
||||
void init (size_t buffSize, std::true_type) {
|
||||
// resize buffer
|
||||
_bufferSize = buffSize;
|
||||
_buf.resize(_bufferSize);
|
||||
_beginIt = std::begin(_buf);
|
||||
}
|
||||
void init (size_t, std::false_type) {
|
||||
_outIt = std::begin(_buf);
|
||||
void init (size_t , std::false_type) {
|
||||
// ignore buffer size parameter, and instead take actual buffer size
|
||||
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
|
||||
}
|
||||
|
||||
std::basic_ios<TChar, CharTraits>* _ios;
|
||||
TBuffer _buf;
|
||||
BufferIt _outIt;
|
||||
BufferIt _beginIt;
|
||||
size_t _currOffset;
|
||||
size_t _bufferSize{0};
|
||||
};
|
||||
|
||||
template <typename TChar, typename Config, typename CharTraits>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 5
|
||||
#define BITSERY_MINOR_VERSION 0
|
||||
#define BITSERY_PATCH_VERSION 0
|
||||
#define BITSERY_PATCH_VERSION 3
|
||||
|
||||
#define BITSERY_QUOTE_MACRO(name) #name
|
||||
#define BITSERY_BUILD_VERSION_STR(major,minor, patch) \
|
||||
|
||||
@@ -192,15 +192,14 @@ namespace bitsery {
|
||||
void writeBytes(const T &v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
writeSwapped(&v, 1, ShouldSwap<typename Adapter::TConfig>{});
|
||||
|
||||
writeSwappedValue(&v, ShouldSwap<typename Adapter::TConfig>{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void writeBuffer(const T *buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
writeSwapped(buf, count, ShouldSwap<typename Adapter::TConfig>{});
|
||||
writeSwappedBuffer(buf, count, ShouldSwap<typename Adapter::TConfig>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -222,16 +221,27 @@ namespace bitsery {
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void writeSwapped(const T *v, size_t count, std::true_type) {
|
||||
void writeSwappedValue(const T *v, std::true_type) {
|
||||
const auto res = details::swap(*v);
|
||||
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeSwappedValue(const T *v, std::false_type) {
|
||||
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(v));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeSwappedBuffer(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(&res), sizeof(T));
|
||||
static_cast<Adapter*>(this)->template writeInternalValue<sizeof(T)>(reinterpret_cast<const typename Adapter::TValue *>(&res));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeSwapped(const T *v, size_t count, std::false_type) {
|
||||
static_cast<Adapter*>(this)->writeInternal(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
|
||||
void writeSwappedBuffer(const T *v, size_t count, std::false_type) {
|
||||
static_cast<Adapter*>(this)->writeInternalBuffer(reinterpret_cast<const typename Adapter::TValue *>(v), count * sizeof(T));
|
||||
}
|
||||
|
||||
};
|
||||
@@ -245,14 +255,16 @@ namespace bitsery {
|
||||
void readBytes(T& v) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(&v, 1);
|
||||
static_cast<Base*>(this)->template readInternalValue<sizeof(T)>(reinterpret_cast<typename Base::TValue *>(&v));
|
||||
swapDataBits(v, ShouldSwap<typename Base::TConfig>{});
|
||||
}
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
void readBuffer(T* buf, size_t count) {
|
||||
static_assert(std::is_integral<T>(), "");
|
||||
static_assert(sizeof(T) == SIZE, "");
|
||||
directRead(buf, count);
|
||||
static_cast<Base*>(this)->readInternalBuffer(reinterpret_cast<typename Base::TValue *>(buf), sizeof(T) * count);
|
||||
swapDataBits(buf, count, ShouldSwap<typename Base::TConfig>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -277,23 +289,26 @@ namespace bitsery {
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directRead(T *v, size_t count) {
|
||||
static_assert(!std::is_const<T>::value, "");
|
||||
static_cast<Base*>(this)->readInternal(reinterpret_cast<typename Base::TValue *>(v), sizeof(T) * count);
|
||||
//swap each byte if necessary
|
||||
_swapDataBits(v, count, ShouldSwap<typename Base::TConfig>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
void swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [](T &x) { x = details::swap(x); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *, size_t , std::false_type) {
|
||||
void swapDataBits(T *, size_t , std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void swapDataBits(T &v, std::true_type) {
|
||||
v = details::swap(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void swapDataBits(T &, std::false_type) {
|
||||
//empty function because no swap is required
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace bitsery {
|
||||
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
auto value{bitsery::Access::create<TValue>()};
|
||||
auto key = bitsery::Access::create<TKey>();
|
||||
auto value = bitsery::Access::create<TValue>();
|
||||
fnc(des, key, value);
|
||||
hint = obj.emplace_hint(hint, std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace bitsery {
|
||||
reserve(obj, size);
|
||||
auto hint = obj.begin();
|
||||
for (auto i = 0u; i < size; ++i) {
|
||||
auto key{bitsery::Access::create<TKey>()};
|
||||
auto key = bitsery::Access::create<TKey>();
|
||||
fnc(des, key);
|
||||
hint = obj.emplace_hint(hint, std::move(key));
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ namespace bitsery {
|
||||
// it just wraps our PolyAllocWithTypeId and pass 0 as typeId
|
||||
// and defines core functions for c++ Allocator concept,
|
||||
template<class T>
|
||||
struct StdPolyAlloc {
|
||||
class StdPolyAlloc {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace bitsery {
|
||||
public:
|
||||
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
|
||||
: _currId{0},
|
||||
_ptrMap{StdPolyAlloc<std::pair<const void*, PLCInfoSerializer>>{memResource}} {}
|
||||
_ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
|
||||
|
||||
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace bitsery {
|
||||
size_t _currId;
|
||||
std::unordered_map<const void*, PLCInfoSerializer,
|
||||
std::hash<const void*>, std::equal_to<const void*>,
|
||||
StdPolyAlloc<std::pair<const void*, PLCInfoSerializer>>
|
||||
StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>
|
||||
> _ptrMap;
|
||||
};
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace bitsery {
|
||||
public:
|
||||
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
|
||||
: _memResource{memResource},
|
||||
_idMap{StdPolyAlloc<std::pair<size_t, PLCInfoDeserializer>>{memResource}} {}
|
||||
_idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
|
||||
|
||||
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace bitsery {
|
||||
MemResourceBase* _memResource;
|
||||
std::unordered_map<size_t, PLCInfoDeserializer,
|
||||
std::hash<size_t>, std::equal_to<size_t>,
|
||||
StdPolyAlloc<std::pair<size_t, PLCInfoDeserializer>>> _idMap;
|
||||
StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>> _idMap;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace bitsery {
|
||||
:pointer_utils::PointerLinkingContextSerialization(memResource),
|
||||
pointer_utils::PointerLinkingContextDeserialization(memResource) {};
|
||||
|
||||
bool isValid() {
|
||||
bool isValid() const {
|
||||
return isPointerSerializationValid() && isPointerDeserializationValid();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,11 +144,10 @@ namespace bitsery {
|
||||
BaseToDerivedKey key{RTTI::template get<TBase>(), RTTI::template get<TDerived>()};
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{_memResource};
|
||||
auto ptr = alloc.allocate(1);
|
||||
std::shared_ptr<THandler> handler(new (ptr)THandler{}, [this](THandler* data) {
|
||||
std::shared_ptr<THandler> handler(new (ptr)THandler{}, [alloc](THandler* data) mutable {
|
||||
data->~THandler();
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{_memResource};
|
||||
alloc.deallocate(data, 1);
|
||||
}, pointer_utils::StdPolyAlloc<THandler>(_memResource));
|
||||
}, alloc);
|
||||
if (_baseToDerivedMap
|
||||
.emplace(key, std::move(handler))
|
||||
.second) {
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace bitsery {
|
||||
container.push_back(::bitsery::Access::create<TValue>());
|
||||
}
|
||||
if (oldSize > newSize) {
|
||||
container.erase(std::next(std::begin(container), newSize));
|
||||
container.erase(std::next(std::begin(container), newSize), std::end(container));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
11
patches/README.md
Normal file
11
patches/README.md
Normal 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`.
|
||||
119
patches/centos7_gcc4.8.2.diff
Normal file
119
patches/centos7_gcc4.8.2.diff
Normal file
@@ -0,0 +1,119 @@
|
||||
diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h
|
||||
index 6d5a441..462cee2 100644
|
||||
--- a/include/bitsery/details/serialization_common.h
|
||||
+++ b/include/bitsery/details/serialization_common.h
|
||||
@@ -380,7 +380,7 @@ namespace bitsery {
|
||||
template <typename ... TArgs>
|
||||
explicit AdapterAndContextRef(Context& ctx, TArgs&& ... args)
|
||||
: _adapter{std::forward<TArgs>(args)...},
|
||||
- _context{ctx}
|
||||
+ _context(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
diff --git a/include/bitsery/ext/inheritance.h b/include/bitsery/ext/inheritance.h
|
||||
index f4c6655..5cd44ab 100644
|
||||
--- a/include/bitsery/ext/inheritance.h
|
||||
+++ b/include/bitsery/ext/inheritance.h
|
||||
@@ -36,7 +36,7 @@ namespace bitsery {
|
||||
class InheritanceContext {
|
||||
public:
|
||||
explicit InheritanceContext(MemResourceBase* memResource = nullptr)
|
||||
- :_virtualBases{pointer_utils::StdPolyAlloc<const void*>{memResource}}
|
||||
+ :_virtualBases{0, std::hash<const void*>{}, std::equal_to<const void*>{}, pointer_utils::StdPolyAlloc<const void*>{memResource}}
|
||||
{}
|
||||
InheritanceContext(const InheritanceContext&) = delete;
|
||||
InheritanceContext&operator = (const InheritanceContext&) = delete;
|
||||
diff --git a/include/bitsery/ext/utils/memory_resource.h b/include/bitsery/ext/utils/memory_resource.h
|
||||
index 472965a..18b3f31 100644
|
||||
--- a/include/bitsery/ext/utils/memory_resource.h
|
||||
+++ b/include/bitsery/ext/utils/memory_resource.h
|
||||
@@ -24,6 +24,7 @@
|
||||
#define BITSERY_EXT_MEMORY_RESOURCE_H
|
||||
|
||||
#include "../../details/serialization_common.h"
|
||||
+#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
namespace bitsery {
|
||||
@@ -128,6 +129,40 @@ namespace bitsery {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
+ using pointer = T*;
|
||||
+ using const_pointer = const T*;
|
||||
+ using reference = T&;
|
||||
+ using const_reference = const T&;
|
||||
+ using size_type = size_t;
|
||||
+ using difference_type = ptrdiff_t;
|
||||
+
|
||||
+ size_t max_size() const noexcept {
|
||||
+ return std::numeric_limits<size_t>::max() / sizeof(value_type);
|
||||
+ }
|
||||
+
|
||||
+ void construct(T *p, const T &val) {
|
||||
+ new((void *) p) T(val);
|
||||
+ }
|
||||
+
|
||||
+ template<class U, class... Args>
|
||||
+ void construct(U *p, Args &&... args) {
|
||||
+ new((void *) p) U(std::forward<Args>(args)...);
|
||||
+ }
|
||||
+
|
||||
+ void destroy(T *p) {
|
||||
+ p->~T();
|
||||
+ }
|
||||
+
|
||||
+ template<class U>
|
||||
+ void destroy(U *p) {
|
||||
+ p->~U();
|
||||
+ }
|
||||
+
|
||||
+ template<typename U>
|
||||
+ struct rebind {
|
||||
+ using other = StdPolyAlloc<U>;
|
||||
+ };
|
||||
+
|
||||
explicit constexpr StdPolyAlloc(MemResourceBase* memResource)
|
||||
:_alloc{memResource} {}
|
||||
explicit constexpr StdPolyAlloc(PolyAllocWithTypeId alloc) : _alloc{alloc} {}
|
||||
diff --git a/include/bitsery/ext/utils/pointer_utils.h b/include/bitsery/ext/utils/pointer_utils.h
|
||||
index f6f90da..6b65600 100644
|
||||
--- a/include/bitsery/ext/utils/pointer_utils.h
|
||||
+++ b/include/bitsery/ext/utils/pointer_utils.h
|
||||
@@ -153,7 +153,7 @@ namespace bitsery {
|
||||
public:
|
||||
explicit PointerLinkingContextSerialization(MemResourceBase* memResource = nullptr)
|
||||
: _currId{0},
|
||||
- _ptrMap{StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
|
||||
+ _ptrMap{0, std::hash<const void*>{}, std::equal_to<const void*>{}, StdPolyAlloc<std::pair<const void* const, PLCInfoSerializer>>{memResource}} {}
|
||||
|
||||
PointerLinkingContextSerialization(const PointerLinkingContextSerialization&) = delete;
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace bitsery {
|
||||
public:
|
||||
explicit PointerLinkingContextDeserialization(MemResourceBase* memResource = nullptr)
|
||||
: _memResource{memResource},
|
||||
- _idMap{StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
|
||||
+ _idMap{0, std::hash<size_t>{}, std::equal_to<size_t>{}, StdPolyAlloc<std::pair<const size_t, PLCInfoDeserializer>>{memResource}} {}
|
||||
|
||||
PointerLinkingContextDeserialization(const PointerLinkingContextDeserialization&) = delete;
|
||||
|
||||
diff --git a/include/bitsery/ext/utils/polymorphism_utils.h b/include/bitsery/ext/utils/polymorphism_utils.h
|
||||
index 6678230..a2cef4d 100644
|
||||
--- a/include/bitsery/ext/utils/polymorphism_utils.h
|
||||
+++ b/include/bitsery/ext/utils/polymorphism_utils.h
|
||||
@@ -185,11 +185,8 @@ namespace bitsery {
|
||||
|
||||
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
|
||||
:_memResource{memResource},
|
||||
- _baseToDerivedMap{pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey,
|
||||
- std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
|
||||
- _baseToDerivedArray{pointer_utils::StdPolyAlloc<std::pair<const size_t,
|
||||
- std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}}
|
||||
- {}
|
||||
+ _baseToDerivedMap{0, BaseToDerivedKeyHashier{}, std::equal_to<BaseToDerivedKey>{}, pointer_utils::StdPolyAlloc<std::pair<const BaseToDerivedKey, std::shared_ptr<PolymorphicHandlerBase>>>{memResource}},
|
||||
+ _baseToDerivedArray{0, std::hash<size_t>{}, std::equal_to<size_t>{}, pointer_utils::StdPolyAlloc<std::pair<const size_t, std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{memResource}} {}
|
||||
|
||||
PolymorphicContext(const PolymorphicContext& ) = delete;
|
||||
PolymorphicContext& operator = (const PolymorphicContext&) = delete;
|
||||
@@ -502,13 +502,13 @@ using BufferedAdapterInternalBufferTypes = ::testing::Types<
|
||||
|
||||
TYPED_TEST_CASE(OutputStreamBuffered, BufferedAdapterInternalBufferTypes);
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferAndRemainingDataToStream) {
|
||||
TYPED_TEST(OutputStreamBuffered, WhenInternalBufferIsFullThenWriteBufferToStream) {
|
||||
uint8_t x{};
|
||||
for (auto i = 0u; i < TestFixture::InternalBufferSize; ++i)
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_TRUE(this->stream.str().empty());
|
||||
this->writer.template writeBytes<1>(x);
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize + 1));
|
||||
EXPECT_THAT(this->stream.str().size(), Eq(TestFixture::InternalBufferSize));
|
||||
}
|
||||
|
||||
TYPED_TEST(OutputStreamBuffered, WhenFlushThenWriteImmediately) {
|
||||
|
||||
@@ -96,6 +96,7 @@ TEST(DeserializeNonDefaultConstructible, ResultContainerShouldShrink) {
|
||||
std::vector<NonDefaultConstructible> res{};
|
||||
res.emplace_back(2);
|
||||
res.emplace_back(3);
|
||||
res.emplace_back(4);
|
||||
|
||||
ctx.createSerializer().container(data, 10);
|
||||
ctx.createDeserializer().container(res, 10);
|
||||
|
||||
Reference in New Issue
Block a user