mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 08:13:56 +00:00
ext function redesign and documentation
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,14 @@
|
||||
<a name="2.1.0"></a>
|
||||
# [2.1.0](https://github.com/fraillt/bitsery/compare/v2.0.0...v2.1.0) (2017-08-07)
|
||||
|
||||
### Features
|
||||
|
||||
* added **SERIALIZE_FRIEND** macro to be able to serialize private struct fields
|
||||
* static_assert when trying to serialize object, that doesn't have **serialize** function defined.
|
||||
* added **custom** function to override default behaviour for **object** serialization
|
||||
* renamed function **ext** to **extension** and changed its interface, to make it more easy to extend
|
||||
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/fraillt/bitsery/compare/v1.1.1...v2.0.0) (2017-07-25)
|
||||
|
||||
@@ -30,10 +41,10 @@
|
||||
### Features
|
||||
|
||||
Serialization functions:
|
||||
* **value** - primitive types (ints, enums, floats)
|
||||
* **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 primitive types (e.g. int between [255..512] will take up 8bits
|
||||
* **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.
|
||||
* **boolBit**/**boolByte** - serialize bool, as 1bit or 1byte.
|
||||
|
||||
18
README.md
18
README.md
@@ -2,16 +2,22 @@
|
||||
|
||||
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.
|
||||
Furthermore knowing your specific requirements
|
||||
|
||||
|
||||
> **bitsery** is looking for your feedback.
|
||||
|
||||
## Features
|
||||
|
||||
**bitsery** is looking for your feedback.
|
||||
* Has configurable endianess support.
|
||||
* Can serialize all common types: arithmetic types, enums, containers and text.
|
||||
* Has advanced features like value ranges and default values.
|
||||
* Is extensible, for types that requires different serialization and deserialization logic (e.g. pointers)
|
||||
* Has error checking on deserialization, and asserts on serialization runtime errors.
|
||||
* Cross-platform compatible
|
||||
* 2-in-1 declarative control flow, same code for serialization and deserialization.
|
||||
* Configurable endianess support.
|
||||
* Advanced serialization features like value ranges and entrophy encoding.
|
||||
* Easy to extend for types that requires different serialization and deserialization logic (e.g. pointers, or geometry compression).
|
||||
* Error checking at runtime on deserialization, and asserts on serialization errors.
|
||||
|
||||
## Example
|
||||
```cpp
|
||||
|
||||
38
doc/README.md
Normal file
38
doc/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
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](design/fundamental_types.md)
|
||||
* [serializer/deserializer functions overloads](design/function_overload.md)
|
||||
* [extending library functionality](design/extensions.md)
|
||||
|
||||
Serializer/Deserializer functions (alphabetical order):
|
||||
* [align](fnc_array.md)
|
||||
* [array](fnc_array.md)
|
||||
* [boolBit/Byte](fnc_bool.md)
|
||||
* [container](fnc_container.md)
|
||||
* [custom](fnc_custom.md)
|
||||
* [extension](fnc_extension.md)
|
||||
* [isValid](fnc_isValid.md)
|
||||
* [object](fnc_object.md)
|
||||
* [range](fnc_range.md)
|
||||
* [substitution](fnc_substitution.md)
|
||||
* [text](fnc_text.md)
|
||||
* [value](fnc_value.md)
|
||||
|
||||
BasicBufferWriter/Reader functions:
|
||||
* [writeBits](bb_write_bits.md)
|
||||
|
||||
Tips and tricks:
|
||||
|
||||
Advanced topics:
|
||||
|
||||
|
||||
|
||||
FAQ:
|
||||
* [Known limitations](limitations.md)
|
||||
|
||||
Other:
|
||||
* [Why Bitsery?](why-bitsery.md)
|
||||
* [Contributing](../CONTRIBUTING.md)
|
||||
* [Change log](../CHANGELOG.md)
|
||||
0
doc/design/extensions.md
Normal file
0
doc/design/extensions.md
Normal file
0
doc/design/function_overload.md
Normal file
0
doc/design/function_overload.md
Normal file
1
doc/design/fundamental_types.md
Normal file
1
doc/design/fundamental_types.md
Normal file
@@ -0,0 +1 @@
|
||||
int char (except bool)
|
||||
9
doc/tutorial/README.md
Normal file
9
doc/tutorial/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
The grand plan for this tutorial is to learn how to serialize/deserialize any object efficiently in time and space, so you could focus on other, more interesting things.
|
||||
|
||||
|
||||
This tutorial will cover these main topics:
|
||||
* [Hello World](hello_world.md) how to set up and to serialize a [fundamental value](../design/fundamental.md).
|
||||
* [2 in 1](two_in_one.md) how to write one control flow for both, serialization and deserialization.
|
||||
* [Squeeze Me!](compression.md) how to compress your data if you know what it stores.
|
||||
* [Anything is Possible](extensions.md) how to extend library for your custom container, compress geometry and more.
|
||||
* [Little or Big](endianness.md) how to change Endianness if you want best performance on PowerPC
|
||||
0
doc/tutorial/hello_world.md
Normal file
0
doc/tutorial/hello_world.md
Normal file
0
doc/tutorial/two_in_one.md
Normal file
0
doc/tutorial/two_in_one.md
Normal file
@@ -27,11 +27,15 @@ struct MyStruct {
|
||||
uint32_t i;
|
||||
MyEnum e;
|
||||
std::vector<float> fs;
|
||||
char16_t x;
|
||||
bool y;
|
||||
};
|
||||
|
||||
//define how object should be serialized/deserialized
|
||||
SERIALIZE(MyStruct) {
|
||||
return s.
|
||||
value1(o.y).
|
||||
value2(o.x).
|
||||
value4(o.i).
|
||||
value2(o.e).
|
||||
container4(o.fs, 10);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#define BITSERY_BITSERY_H
|
||||
|
||||
#define BITSERY_MAJOR_VERSION 2
|
||||
#define BITSERY_MINOR_VERSION 0
|
||||
#define BITSERY_MINOR_VERSION 1
|
||||
#define BITSERY_PATCH_VERSION 0
|
||||
|
||||
#define BITSERY_QUOTE_MACRO(name) #name
|
||||
|
||||
@@ -32,31 +32,34 @@
|
||||
|
||||
namespace bitsery {
|
||||
|
||||
template <typename Config>
|
||||
template<typename Config>
|
||||
struct BasicBufferReader {
|
||||
using ValueType = typename Config::BufferValueType;
|
||||
using ScratchType = typename Config::BufferScrathType;
|
||||
|
||||
BasicBufferReader(const ValueType* data, size_t size) : _pos{data}, _end{data + size}
|
||||
{
|
||||
BasicBufferReader(const ValueType *data, size_t size) : _pos{data}, _end{data + size} {
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(ValueType)*2 == sizeof(ScratchType), "ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
explicit BasicBufferReader(const std::vector<ValueType> &buf) : BasicBufferReader(buf.data(), buf.size()) {
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
explicit BasicBufferReader(const ValueType (&data)[N]): BasicBufferReader(data, N)
|
||||
{
|
||||
template<size_t N>
|
||||
explicit BasicBufferReader(const ValueType (&data)[N]): BasicBufferReader(data, N) {
|
||||
}
|
||||
|
||||
BasicBufferReader(const BasicBufferReader&) = delete;
|
||||
BasicBufferReader& operator=(const BasicBufferReader& ) = delete;
|
||||
BasicBufferReader(BasicBufferReader&&) noexcept = default;
|
||||
BasicBufferReader& operator=(BasicBufferReader&&) noexcept = default;
|
||||
BasicBufferReader(const BasicBufferReader &) = delete;
|
||||
|
||||
BasicBufferReader &operator=(const BasicBufferReader &) = delete;
|
||||
|
||||
BasicBufferReader(BasicBufferReader &&) noexcept = default;
|
||||
|
||||
BasicBufferReader &operator=(BasicBufferReader &&) noexcept = default;
|
||||
|
||||
~BasicBufferReader() noexcept = default;
|
||||
|
||||
|
||||
@@ -116,8 +119,8 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
private:
|
||||
const ValueType* _pos;
|
||||
const ValueType* _end;
|
||||
const ValueType *_pos;
|
||||
const ValueType *_end;
|
||||
|
||||
template<typename T>
|
||||
bool directRead(T *v, size_t count) {
|
||||
@@ -136,7 +139,7 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
void _swapDataBits(T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](T& v) { v = details::swap(v); });
|
||||
std::for_each(v, std::next(v, count), [this](T &v) { v = details::swap(v); });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -57,6 +57,14 @@ namespace bitsery {
|
||||
_bitsCount += details::BITS_SIZE<T> * count;
|
||||
}
|
||||
|
||||
void align() {
|
||||
_bitsCount += 8 - (_bitsCount % 8);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
|
||||
}
|
||||
|
||||
//get size in bytes
|
||||
size_t getSize() const {
|
||||
return _bitsCount / 8;
|
||||
@@ -67,7 +75,7 @@ namespace bitsery {
|
||||
|
||||
};
|
||||
|
||||
template <typename Config>
|
||||
template<typename Config>
|
||||
struct BasicBufferWriter {
|
||||
using ValueType = typename Config::BufferValueType;
|
||||
using ScratchType = typename Config::BufferScrathType;
|
||||
@@ -75,14 +83,19 @@ namespace bitsery {
|
||||
explicit BasicBufferWriter(std::vector<ValueType> &buffer) : _outIt{std::back_inserter(buffer)} {
|
||||
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
|
||||
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
|
||||
static_assert(sizeof(ValueType)*2 == sizeof(ScratchType), "ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
|
||||
"ScratchType must be 2x bigger than value type");
|
||||
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
|
||||
}
|
||||
|
||||
BasicBufferWriter(const BasicBufferWriter&) = delete;
|
||||
BasicBufferWriter& operator=(const BasicBufferWriter& ) = delete;
|
||||
BasicBufferWriter(BasicBufferWriter&&) noexcept = default;
|
||||
BasicBufferWriter& operator=(BasicBufferWriter&&) noexcept = default;
|
||||
BasicBufferWriter(const BasicBufferWriter &) = delete;
|
||||
|
||||
BasicBufferWriter &operator=(const BasicBufferWriter &) = delete;
|
||||
|
||||
BasicBufferWriter(BasicBufferWriter &&) noexcept = default;
|
||||
|
||||
BasicBufferWriter &operator=(BasicBufferWriter &&) noexcept = default;
|
||||
|
||||
~BasicBufferWriter() noexcept = default;
|
||||
|
||||
template<size_t SIZE, typename T>
|
||||
@@ -139,22 +152,22 @@ namespace bitsery {
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
void directWrite(T&& v, size_t count) {
|
||||
void directWrite(T &&v, size_t count) {
|
||||
_directWriteSwapTag(std::forward<T>(v), count, std::integral_constant<bool,
|
||||
Config::NetworkEndianness != details::getSystemEndianness()>{});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
|
||||
std::for_each(v, std::next(v, count), [this](const T& v) {
|
||||
std::for_each(v, std::next(v, count), [this](const T &v) {
|
||||
const auto res = details::swap(v);
|
||||
std::copy_n(reinterpret_cast<const ValueType*>(&res), sizeof(T), _outIt);
|
||||
std::copy_n(reinterpret_cast<const ValueType *>(&res), sizeof(T), _outIt);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _directWriteSwapTag(const T *v, size_t count, std::false_type) {
|
||||
std::copy_n(reinterpret_cast<const ValueType*>(v), count * sizeof(T), _outIt);
|
||||
std::copy_n(reinterpret_cast<const ValueType *>(v), count * sizeof(T), _outIt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -38,10 +38,15 @@ namespace bitsery {
|
||||
* serializer macro, serialize function specialization that accepts T& and const T&
|
||||
*/
|
||||
|
||||
|
||||
#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)
|
||||
|
||||
#define SERIALIZE_FRIEND(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> \
|
||||
friend S& serialize(S& s, T& o)
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //BITSERY_COMMON_H
|
||||
|
||||
@@ -35,57 +35,84 @@ namespace bitsery {
|
||||
template<typename Reader>
|
||||
class Deserializer {
|
||||
public:
|
||||
Deserializer(Reader& r):_reader{r}, _isValid{true} {};
|
||||
Deserializer(Reader &r) : _reader{r}, _isValid{true} {};
|
||||
|
||||
template <typename T>
|
||||
Deserializer& object(T&& obj) {
|
||||
return serialize(*this, std::forward<T>(obj));
|
||||
/*
|
||||
* object function
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
Deserializer &object(T &&obj) {
|
||||
return details::SerializeFunction<Deserializer, T>::invoke(*this, std::forward<T>(obj));
|
||||
}
|
||||
|
||||
//in c++17 change "class" to typename
|
||||
template <template <typename> class Extension, typename TValue, typename Fnc>
|
||||
Deserializer& ext(TValue& v, Fnc&& fnc) {
|
||||
static_assert(!std::is_const<TValue>(), "");
|
||||
Extension<TValue> ext{v};
|
||||
ext.deserialize(*this, std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
};
|
||||
|
||||
/*
|
||||
* value overloads
|
||||
*/
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
Deserializer &value(T &v) {
|
||||
static_assert(std::numeric_limits<T>::is_iec559, "");
|
||||
if (_isValid) {
|
||||
_isValid = _reader.template readBytes<VSIZE>(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T>&>(v));
|
||||
_isValid = _reader.template readBytes<VSIZE>(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
Deserializer &value(T &v) {
|
||||
using UT = std::underlying_type_t<T>;
|
||||
if (_isValid) {
|
||||
_isValid = _reader.template readBytes<VSIZE>(reinterpret_cast<UT&>(v));
|
||||
_isValid = _reader.template readBytes<VSIZE>(reinterpret_cast<UT &>(v));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
Deserializer& value(T& v) {
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
Deserializer &value(T &v) {
|
||||
if (_isValid) {
|
||||
_isValid = _reader.template readBytes<VSIZE>(v);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* custom function
|
||||
*/
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
Deserializer &custom(T &&obj, Fnc &&fnc) {
|
||||
fnc(*this, std::forward<T>(obj));
|
||||
return *this;
|
||||
};
|
||||
|
||||
/*
|
||||
* extension functions
|
||||
*/
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
Deserializer &extension(T &obj, Ext &&ext, Fnc &&fnc) {
|
||||
ext.deserialize(obj, *this, std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
Deserializer &extension(T &obj, Ext &&ext) {
|
||||
ext.deserialize(obj, *this, [](auto &s, auto &v) { s.template value<VSIZE>(v); });
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Deserializer &extension(T &obj, Ext &&ext) {
|
||||
ext.deserialize(obj, *this, [](auto &s, auto &v) { s.object(v); });
|
||||
return *this;
|
||||
};
|
||||
|
||||
/*
|
||||
* bool
|
||||
*/
|
||||
|
||||
Deserializer& boolBit(bool& v) {
|
||||
Deserializer &boolBit(bool &v) {
|
||||
if (_isValid) {
|
||||
unsigned char tmp;
|
||||
_isValid = _reader.readBits(tmp, 1);
|
||||
@@ -94,7 +121,7 @@ namespace bitsery {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Deserializer& boolByte(bool& v) {
|
||||
Deserializer &boolByte(bool &v) {
|
||||
if (_isValid) {
|
||||
unsigned char tmp;
|
||||
_isValid = _reader.template readBytes<1>(tmp);
|
||||
@@ -109,10 +136,10 @@ namespace bitsery {
|
||||
* range
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
Deserializer& range(T& v, const RangeSpec<T>& range) {
|
||||
template<typename T>
|
||||
Deserializer &range(T &v, const RangeSpec <T> &range) {
|
||||
if (_isValid) {
|
||||
_isValid = _reader.readBits(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T>&>(v), range.bitsRequired);
|
||||
_isValid = _reader.readBits(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v), range.bitsRequired);
|
||||
details::setRangeValue(v, range);
|
||||
if (_isValid)
|
||||
_isValid = details::isRangeValid(v, range);
|
||||
@@ -124,12 +151,12 @@ namespace bitsery {
|
||||
* substitution overloads
|
||||
*/
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues, Fnc&& 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];
|
||||
v = expectedValues[index - 1];
|
||||
else
|
||||
fnc(*this, v);
|
||||
}
|
||||
@@ -137,12 +164,12 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues) {
|
||||
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];
|
||||
v = expectedValues[index - 1];
|
||||
else
|
||||
value<VSIZE>(v);
|
||||
}
|
||||
@@ -150,12 +177,12 @@ namespace bitsery {
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer& substitution(T& v, const std::array<T,N>& expectedValues) {
|
||||
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];
|
||||
v = expectedValues[index - 1];
|
||||
else
|
||||
object(v);
|
||||
}
|
||||
@@ -166,8 +193,8 @@ namespace bitsery {
|
||||
* text overloads
|
||||
*/
|
||||
|
||||
template <size_t VSIZE, typename T>
|
||||
Deserializer& text(std::basic_string<T>& str, size_t maxSize) {
|
||||
template<size_t VSIZE, typename T>
|
||||
Deserializer &text(std::basic_string<T> &str, size_t maxSize) {
|
||||
size_t size;
|
||||
readSize(size, maxSize);
|
||||
if (_isValid) {
|
||||
@@ -178,9 +205,9 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Deserializer& text(T (&str)[N]) {
|
||||
Deserializer &text(T (&str)[N]) {
|
||||
size_t size;
|
||||
readSize(size, N-1);
|
||||
readSize(size, N - 1);
|
||||
if (_isValid) {
|
||||
auto first = std::begin(str);
|
||||
procContainer<VSIZE>(first, std::next(first, size), std::true_type{});
|
||||
@@ -194,8 +221,8 @@ namespace bitsery {
|
||||
* container overloads
|
||||
*/
|
||||
|
||||
template <typename T, typename Fnc>
|
||||
Deserializer& container(T&& obj, size_t maxSize, Fnc&& fnc) {
|
||||
template<typename T, typename Fnc>
|
||||
Deserializer &container(T &&obj, size_t maxSize, Fnc &&fnc) {
|
||||
decltype(obj.size()) size{};
|
||||
readSize(size, maxSize);
|
||||
if (_isValid) {
|
||||
@@ -205,8 +232,8 @@ namespace bitsery {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t VSIZE, typename T>
|
||||
Deserializer& container(T& obj, size_t maxSize) {
|
||||
template<size_t VSIZE, typename T>
|
||||
Deserializer &container(T &obj, size_t maxSize) {
|
||||
decltype(obj.size()) size{};
|
||||
readSize(size, maxSize);
|
||||
if (_isValid) {
|
||||
@@ -216,8 +243,8 @@ namespace bitsery {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Deserializer& container(T& obj, size_t maxSize) {
|
||||
template<typename T>
|
||||
Deserializer &container(T &obj, size_t maxSize) {
|
||||
decltype(obj.size()) size{};
|
||||
readSize(size, maxSize);
|
||||
if (_isValid) {
|
||||
@@ -234,19 +261,19 @@ namespace bitsery {
|
||||
//std::array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer& array(std::array<T,N> &arr, Fnc && fnc) {
|
||||
Deserializer &array(std::array<T, N> &arr, Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Deserializer& array(std::array<T,N> &arr) {
|
||||
Deserializer &array(std::array<T, N> &arr) {
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer& array(std::array<T,N> &arr) {
|
||||
Deserializer &array(std::array<T, N> &arr) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
return *this;
|
||||
}
|
||||
@@ -254,69 +281,134 @@ namespace bitsery {
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Deserializer& array(T (&arr)[N], Fnc&& fnc) {
|
||||
Deserializer &array(T (&arr)[N], Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Deserializer& array(T (&arr)[N]) {
|
||||
Deserializer &array(T (&arr)[N]) {
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer& array(T (&arr)[N]) {
|
||||
Deserializer &array(T (&arr)[N]) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
return *this;
|
||||
}
|
||||
bool isValid() const {
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
Deserializer& align() {
|
||||
Deserializer &align() {
|
||||
_reader.align();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
//overloads for functions with explicit type size
|
||||
template<typename T> Deserializer& value1(T &&v) { return value<1>(std::forward<T>(v)); }
|
||||
template<typename T> Deserializer& value2(T &&v) { return value<2>(std::forward<T>(v)); }
|
||||
template<typename T> Deserializer& value4(T &&v) { return value<4>(std::forward<T>(v)); }
|
||||
template<typename T> Deserializer& value8(T &&v) { return value<8>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, size_t N> Deserializer& substitution1
|
||||
(T &v, const std::array<T, N> &expectedValues) { return substitution<1>(v, expectedValues); };
|
||||
template<typename T, size_t N> Deserializer& substitution2
|
||||
(T &v, const std::array<T, N> &expectedValues) { return substitution<2>(v, expectedValues); };
|
||||
template<typename T, size_t N> Deserializer& substitution4
|
||||
(T &v, const std::array<T, N> &expectedValues) { return substitution<4>(v, expectedValues); };
|
||||
template<typename T, size_t N> Deserializer& substitution8
|
||||
(T &v, const std::array<T, N> &expectedValues) { return substitution<8>(v, expectedValues); };
|
||||
template<typename T>
|
||||
Deserializer &value1(T &&v) { return value<1>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T> Deserializer& text1(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<1>(str, maxSize); }
|
||||
template<typename T> Deserializer& text2(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<2>(str, maxSize); }
|
||||
template<typename T> Deserializer& text4(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<4>(str, maxSize); }
|
||||
template<typename T>
|
||||
Deserializer &value2(T &&v) { return value<2>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, size_t N> Deserializer& text1(T (&str)[N]) { return text<1>(str); }
|
||||
template<typename T, size_t N> Deserializer& text2(T (&str)[N]) { return text<2>(str); }
|
||||
template<typename T, size_t N> Deserializer& text4(T (&str)[N]) { return text<4>(str); }
|
||||
template<typename T>
|
||||
Deserializer &value4(T &&v) { return value<4>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T> Deserializer& container1(T &&obj, size_t maxSize) {
|
||||
return container<1>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Deserializer& container2(T &&obj, size_t maxSize) {
|
||||
return container<2>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Deserializer& container4(T &&obj, size_t maxSize) {
|
||||
return container<4>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Deserializer& container8(T &&obj, size_t maxSize) {
|
||||
return container<8>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T>
|
||||
Deserializer &value8(T &&v) { return value<8>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Deserializer &extension1(T &v, Ext &&ext) {
|
||||
return extension<1>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Deserializer &extension2(T &v, Ext &&ext) {
|
||||
return extension<2>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Deserializer &extension4(T &v, Ext &&ext) {
|
||||
return extension<4>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Deserializer &extension8(T &v, Ext &&ext) {
|
||||
return extension<8>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &substitution1(T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<1>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &substitution2(T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<2>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &substitution4(T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<4>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &substitution8(T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<8>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Deserializer &text1(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<1>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer &text2(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<2>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer &text4(std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<4>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &text1(T (&str)[N]) { return text<1>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &text2(T (&str)[N]) { return text<2>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Deserializer &text4(T (&str)[N]) { return text<4>(str); }
|
||||
|
||||
template<typename T>
|
||||
Deserializer &container1(T &&obj, size_t maxSize) {
|
||||
return container<1>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer &container2(T &&obj, size_t maxSize) {
|
||||
return container<2>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer &container4(T &&obj, size_t maxSize) {
|
||||
return container<4>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Deserializer &container8(T &&obj, size_t maxSize) {
|
||||
return container<8>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
private:
|
||||
Reader& _reader;
|
||||
Reader &_reader;
|
||||
bool _isValid;
|
||||
|
||||
void readSize(size_t &size, size_t maxSize) {
|
||||
size = {};
|
||||
if (_isValid) {
|
||||
@@ -330,9 +422,9 @@ namespace bitsery {
|
||||
_isValid = _reader.readBits(secondBit, 1);
|
||||
if (_isValid) {
|
||||
if (secondBit) {
|
||||
_isValid = _reader.readBits(size,14);
|
||||
_isValid = _reader.readBits(size, 14);
|
||||
} else {
|
||||
_isValid = _reader.readBits(size,30);
|
||||
_isValid = _reader.readBits(size, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,7 +438,7 @@ namespace bitsery {
|
||||
//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 (;_isValid && first != last; ++first)
|
||||
for (; _isValid && first != last; ++first)
|
||||
value<VSIZE>(*first);
|
||||
};
|
||||
|
||||
@@ -361,14 +453,14 @@ namespace bitsery {
|
||||
//process by calling functions
|
||||
template<typename It, typename Fnc>
|
||||
void procContainer(It first, It last, Fnc fnc) {
|
||||
for (;_isValid && first != last; ++first)
|
||||
for (; _isValid && first != last; ++first)
|
||||
fnc(*this, *first);
|
||||
};
|
||||
|
||||
//process object types
|
||||
template<typename It>
|
||||
void procContainer(It first, It last) {
|
||||
for (;_isValid && first != last; ++first)
|
||||
for (; _isValid && first != last; ++first)
|
||||
object(*first);
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace bitsery {
|
||||
struct swapImpl {
|
||||
static uint64_t exec(uint64_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64( value );
|
||||
return __builtin_bswap64(value);
|
||||
#else
|
||||
value = ( value & 0x00000000FFFFFFFF ) << 32 | ( value & 0xFFFFFFFF00000000 ) >> 32;
|
||||
value = ( value & 0x0000FFFF0000FFFF ) << 16 | ( value & 0xFFFF0000FFFF0000 ) >> 16;
|
||||
@@ -54,18 +54,16 @@ namespace bitsery {
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t exec(uint32_t value)
|
||||
{
|
||||
static uint32_t exec(uint32_t value) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32( value );
|
||||
return __builtin_bswap32(value);
|
||||
#else
|
||||
return ( value & 0x000000ff ) << 24 | ( value & 0x0000ff00 ) << 8 | ( value & 0x00ff0000 ) >> 8 | ( value & 0xff000000 ) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint16_t exec(uint16_t value)
|
||||
{
|
||||
return ( value & 0x00ff ) << 8 | ( value & 0xff00 ) >> 8;
|
||||
static uint16_t exec(uint16_t value) {
|
||||
return (value & 0x00ff) << 8 | (value & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
static uint8_t exec(uint8_t value) {
|
||||
@@ -73,24 +71,26 @@ namespace bitsery {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TValue>
|
||||
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;
|
||||
typename std::conditional<TSize == 4, uint32_t, uint64_t>::type>::type>::type;
|
||||
return swapImpl::exec(static_cast<UT>(value));
|
||||
}
|
||||
|
||||
//add test data in separate struct, because some compilers only support constexpr functions with return-only body
|
||||
struct EndiannessTestData {
|
||||
static constexpr uint32_t _sample4Bytes = 0x01020304;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t&)_sample4Bytes;
|
||||
static constexpr uint8_t _sample1stByte = (const uint8_t &) _sample4Bytes;
|
||||
};
|
||||
|
||||
constexpr EndiannessType getSystemEndianness() {
|
||||
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01, "system must be either little or big endian");
|
||||
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian : EndiannessType::BigEndian;
|
||||
static_assert(EndiannessTestData::_sample1stByte == 0x04 || EndiannessTestData::_sample1stByte == 0x01,
|
||||
"system must be either little or big endian");
|
||||
return EndiannessTestData::_sample1stByte == 0x04 ? EndiannessType::LittleEndian
|
||||
: EndiannessType::BigEndian;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace bitsery {
|
||||
template<typename T>
|
||||
using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE<T>::type;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
constexpr size_t getSize(T v, size_t s) {
|
||||
return v > 0 ? getSize(v / 2, s + 1) : s;
|
||||
}
|
||||
@@ -149,21 +149,21 @@ namespace bitsery {
|
||||
return static_cast<VT>(ratio * maxUint);
|
||||
};
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
|
||||
void setRangeValue(T& v, const RangeSpec<T>& r) {
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
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 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);
|
||||
};
|
||||
@@ -195,6 +195,26 @@ namespace bitsery {
|
||||
return 0u;
|
||||
};
|
||||
|
||||
/*
|
||||
* functions for object serialization
|
||||
*/
|
||||
|
||||
template<typename S, typename T, typename Enabled = void>
|
||||
struct SerializeFunction {
|
||||
static S &invoke(S &s, T &v) {
|
||||
static_assert(!std::is_void<Enabled>::value, "please define 'serialize' function.");
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
struct SerializeFunction<S, T, typename std::enable_if<
|
||||
std::is_same<S &, decltype(serialize(std::declval<S &>(), std::declval<T &>()))>::value
|
||||
>::type> {
|
||||
static S &invoke(S &s, T &v) {
|
||||
return serialize(s, v);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* delta functions
|
||||
@@ -205,7 +225,8 @@ namespace bitsery {
|
||||
|
||||
template<typename T>
|
||||
ObjectMemoryPosition(const T &oldObj, const T &newObj)
|
||||
:ObjectMemoryPosition{reinterpret_cast<const char *>(&oldObj), reinterpret_cast<const char *>(&newObj),
|
||||
:ObjectMemoryPosition{reinterpret_cast<const char *>(&oldObj),
|
||||
reinterpret_cast<const char *>(&newObj),
|
||||
sizeof(T)} {
|
||||
}
|
||||
|
||||
|
||||
@@ -24,40 +24,52 @@
|
||||
#ifndef BITSERY_EXT_OPTIONAL_H
|
||||
#define BITSERY_EXT_OPTIONAL_H
|
||||
|
||||
|
||||
//this module do not include optional, but expects it to be declared in std::optional
|
||||
//if you're using experimental optional from <experimental/optional>
|
||||
//add it in std namespace like this:
|
||||
//namespace std {
|
||||
// template <typename T>
|
||||
// using optional = experimental::optional<T>;
|
||||
//}
|
||||
namespace bitsery {
|
||||
namespace ext {
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
using std_optional = ::std::optional<T>;
|
||||
|
||||
template <typename T>
|
||||
class optional {
|
||||
public:
|
||||
explicit optional(T& v):_value{v} {
|
||||
template<typename T>
|
||||
constexpr void assertType() const {
|
||||
using TOpt = typename std::remove_cv<T>::type;
|
||||
using TVal = typename TOpt::value_type;
|
||||
static_assert(std::is_same<TOpt, std_optional<TVal>>(), "");
|
||||
static_assert(std::is_default_constructible<TVal>::value, "");
|
||||
};
|
||||
template <typename TSerializer, typename Fnc>
|
||||
void serialize(TSerializer& ser, const Fnc& fnc) {
|
||||
ser.boolByte(static_cast<bool>(_value));
|
||||
if (_value)
|
||||
fnc(ser, *_value);
|
||||
|
||||
template<typename T, typename Ser, typename Fnc>
|
||||
void serialize(const T &obj, Ser &ser, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
ser.boolByte(static_cast<bool>(obj));
|
||||
if (obj)
|
||||
fnc(ser, *obj);
|
||||
}
|
||||
template <typename TSerializer, typename Fnc>
|
||||
void deserialize(TSerializer& ser, const Fnc& fnc) {
|
||||
|
||||
template<typename T, typename Des, typename Fnc>
|
||||
void deserialize(T &obj, Des &des, Fnc &&fnc) const {
|
||||
assertType<T>();
|
||||
bool exists{};
|
||||
ser.boolByte(exists);
|
||||
des.boolByte(exists);
|
||||
if (exists) {
|
||||
typename T::value_type tmp{};
|
||||
fnc(ser, tmp);
|
||||
_value = tmp;
|
||||
fnc(des, tmp);
|
||||
obj = tmp;
|
||||
} else {
|
||||
_value = T{};
|
||||
//experimental optional doesnt have .reset method
|
||||
obj = T{};
|
||||
}
|
||||
}
|
||||
private:
|
||||
T& _value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -38,53 +38,79 @@ namespace bitsery {
|
||||
public:
|
||||
Serializer(Writter &w) : _writter{w} {};
|
||||
|
||||
/*
|
||||
* object function
|
||||
*/
|
||||
template<typename T>
|
||||
Serializer& object(const T &obj) {
|
||||
return serialize(*this, obj);
|
||||
Serializer &object(T &&obj) {
|
||||
return details::SerializeFunction<Serializer, T>::invoke(*this, std::forward<T>(obj));
|
||||
}
|
||||
|
||||
//in c++17 change "class" to typename
|
||||
template <template <typename> class Extension, typename TValue, typename Fnc>
|
||||
Serializer& ext(const TValue& v, Fnc&& fnc ) {
|
||||
Extension<const TValue> ext{v};
|
||||
ext.serialize(*this, std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* value overloads
|
||||
*/
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
|
||||
Serializer& value(const T &v) {
|
||||
Serializer &value(const T &v) {
|
||||
static_assert(std::numeric_limits<T>::is_iec559, "");
|
||||
_writter.template writeBytes<VSIZE>(reinterpret_cast<const details::SAME_SIZE_UNSIGNED<T> &>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
|
||||
Serializer& value(const T &v) {
|
||||
Serializer &value(const T &v) {
|
||||
_writter.template writeBytes<VSIZE>(reinterpret_cast<const std::underlying_type_t<T> &>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
|
||||
Serializer& value(const T &v) {
|
||||
Serializer &value(const T &v) {
|
||||
_writter.template writeBytes<VSIZE>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* custom function
|
||||
*/
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
Serializer &custom(T &&obj, Fnc &&fnc) {
|
||||
fnc(*this, std::forward<T>(obj));
|
||||
return *this;
|
||||
};
|
||||
|
||||
/*
|
||||
* extension functions
|
||||
*/
|
||||
|
||||
template<typename T, typename Ext, typename Fnc>
|
||||
Serializer &extension(const T &obj, Ext &&ext, Fnc &&fnc) {
|
||||
ext.serialize(obj, *this, std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, typename Ext>
|
||||
Serializer &extension(const T &obj, Ext &&ext) {
|
||||
ext.serialize(obj, *this, [](auto &s, auto &v) { s.template value<VSIZE>(v); });
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Serializer &extension(const T &obj, Ext &&ext) {
|
||||
ext.serialize(obj, *this, [](auto &s, auto &v) { s.object(v); });
|
||||
return *this;
|
||||
};
|
||||
|
||||
/*
|
||||
* bool
|
||||
*/
|
||||
|
||||
Serializer& boolBit(bool v) {
|
||||
Serializer &boolBit(bool v) {
|
||||
_writter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Serializer& boolByte(bool v) {
|
||||
Serializer &boolByte(bool v) {
|
||||
_writter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
|
||||
return *this;
|
||||
}
|
||||
@@ -94,9 +120,10 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
Serializer& range(const T &v, const RangeSpec<T> &range) {
|
||||
Serializer &range(const T &v, const RangeSpec<T> &range) {
|
||||
assert(details::isRangeValid(v, range));
|
||||
_writter.template writeBits<decltype(details::getRangeValue(v, range))>(details::getRangeValue(v, range), range.bitsRequired);
|
||||
_writter.template writeBits<decltype(details::getRangeValue(v, range))>(details::getRangeValue(v, range),
|
||||
range.bitsRequired);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -104,27 +131,27 @@ namespace bitsery {
|
||||
* substitution overloads
|
||||
*/
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
|
||||
Serializer &substitution(const T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
|
||||
auto index = details::findSubstitutionIndex(v, expectedValues);
|
||||
range(index, {{}, N +1});
|
||||
range(index, {{}, N + 1});
|
||||
if (!index)
|
||||
fnc(*this, v);
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues) {
|
||||
Serializer &substitution(const T &v, const std::array<T, N> &expectedValues) {
|
||||
auto index = details::findSubstitutionIndex(v, expectedValues);
|
||||
range(index, {{}, N +1});
|
||||
range(index, {{}, N + 1});
|
||||
if (!index)
|
||||
value<VSIZE>(v);
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer& substitution(const T &v, const std::array<T, N> &expectedValues) {
|
||||
Serializer &substitution(const T &v, const std::array<T, N> &expectedValues) {
|
||||
auto index = details::findSubstitutionIndex(v, expectedValues);
|
||||
range(index, {{}, N +1});
|
||||
range(index, {{}, N + 1});
|
||||
if (!index)
|
||||
object(v);
|
||||
return *this;
|
||||
@@ -135,7 +162,7 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
Serializer& text(const std::basic_string<T> &str, size_t maxSize) {
|
||||
Serializer &text(const std::basic_string<T> &str, size_t maxSize) {
|
||||
assert(str.size() <= maxSize);
|
||||
auto first = std::begin(str);
|
||||
auto last = std::end(str);
|
||||
@@ -145,7 +172,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Serializer& text(const T (&str)[N]) {
|
||||
Serializer &text(const T (&str)[N]) {
|
||||
auto first = std::begin(str);
|
||||
auto last = std::next(first, std::min(std::char_traits<T>::length(str), N - 1));
|
||||
writeSize(std::distance(first, last));
|
||||
@@ -158,7 +185,7 @@ namespace bitsery {
|
||||
*/
|
||||
|
||||
template<typename T, typename Fnc>
|
||||
Serializer& container(const T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
Serializer &container(const T &obj, size_t maxSize, Fnc &&fnc) {
|
||||
assert(obj.size() <= maxSize);
|
||||
writeSize(obj.size());
|
||||
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
|
||||
@@ -166,7 +193,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T>
|
||||
Serializer& container(const T &obj, size_t maxSize) {
|
||||
Serializer &container(const T &obj, size_t maxSize) {
|
||||
static_assert(VSIZE > 0, "");
|
||||
assert(obj.size() <= maxSize);
|
||||
writeSize(obj.size());
|
||||
@@ -176,7 +203,7 @@ namespace bitsery {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer& container(const T &obj, size_t maxSize) {
|
||||
Serializer &container(const T &obj, size_t maxSize) {
|
||||
assert(obj.size() <= maxSize);
|
||||
writeSize(obj.size());
|
||||
procContainer(std::begin(obj), std::end(obj));
|
||||
@@ -190,20 +217,20 @@ namespace bitsery {
|
||||
//std::array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer& array(const std::array<T, N> &arr, Fnc &&fnc) {
|
||||
Serializer &array(const std::array<T, N> &arr, Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Serializer& array(const std::array<T, N> &arr) {
|
||||
Serializer &array(const std::array<T, N> &arr) {
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer& array(const std::array<T, N> &arr) {
|
||||
Serializer &array(const std::array<T, N> &arr) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
return *this;
|
||||
}
|
||||
@@ -211,73 +238,156 @@ namespace bitsery {
|
||||
//c-style array overloads
|
||||
|
||||
template<typename T, size_t N, typename Fnc>
|
||||
Serializer& array(const T (&arr)[N], Fnc &&fnc) {
|
||||
Serializer &array(const T (&arr)[N], Fnc &&fnc) {
|
||||
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t VSIZE, typename T, size_t N>
|
||||
Serializer& array(const T (&arr)[N]) {
|
||||
Serializer &array(const T (&arr)[N]) {
|
||||
static_assert(VSIZE > 0, "");
|
||||
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer& array(const T (&arr)[N]) {
|
||||
Serializer &array(const T (&arr)[N]) {
|
||||
procContainer(std::begin(arr), std::end(arr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Serializer& align() {
|
||||
Serializer &align() {
|
||||
_writter.align();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
//serialization cannot fail, it doesn't handle out of memory exception
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//overloads for functions with explicit type size
|
||||
template<typename T> Serializer& value1(T &&v) { return value<1>(std::forward<T>(v)); }
|
||||
template<typename T> Serializer& value2(T &&v) { return value<2>(std::forward<T>(v)); }
|
||||
template<typename T> Serializer& value4(T &&v) { return value<4>(std::forward<T>(v)); }
|
||||
template<typename T> Serializer& value8(T &&v) { return value<8>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, size_t N> Serializer& substitution1
|
||||
(const T &v, const std::array<T, N> &expectedValues) { return substitution<1>(v, expectedValues); };
|
||||
template<typename T, size_t N> Serializer& substitution2
|
||||
(const T &v, const std::array<T, N> &expectedValues) { return substitution<2>(v, expectedValues); };
|
||||
template<typename T, size_t N> Serializer& substitution4
|
||||
(const T &v, const std::array<T, N> &expectedValues) { return substitution<4>(v, expectedValues); };
|
||||
template<typename T, size_t N> Serializer& substitution8
|
||||
(const T &v, const std::array<T, N> &expectedValues) { return substitution<8>(v, expectedValues); };
|
||||
template<typename T>
|
||||
Serializer &value1(T &&v) { return value<1>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T> Serializer& text1(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<1>(str, maxSize); }
|
||||
template<typename T> Serializer& text2(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<2>(str, maxSize); }
|
||||
template<typename T> Serializer& text4(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<4>(str, maxSize); }
|
||||
template<typename T>
|
||||
Serializer &value2(T &&v) { return value<2>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, size_t N> Serializer& text1(const T (&str)[N]) { return text<1>(str); }
|
||||
template<typename T, size_t N> Serializer& text2(const T (&str)[N]) { return text<2>(str); }
|
||||
template<typename T, size_t N> Serializer& text4(const T (&str)[N]) { return text<4>(str); }
|
||||
template<typename T>
|
||||
Serializer &value4(T &&v) { return value<4>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T> Serializer& container1(T &&obj, size_t maxSize) {
|
||||
return container<1>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Serializer& container2(T &&obj, size_t maxSize) {
|
||||
return container<2>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Serializer& container4(T &&obj, size_t maxSize) {
|
||||
return container<4>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T> Serializer& container8(T &&obj, size_t maxSize) {
|
||||
return container<8>(std::forward<T>(obj), maxSize); }
|
||||
template<typename T>
|
||||
Serializer &value8(T &&v) { return value<8>(std::forward<T>(v)); }
|
||||
|
||||
template<typename T, size_t N> Serializer& array1(const std::array<T, N> &arr) { return array<1>(arr); }
|
||||
template<typename T, size_t N> Serializer& array2(const std::array<T, N> &arr) { return array<2>(arr); }
|
||||
template<typename T, size_t N> Serializer& array4(const std::array<T, N> &arr) { return array<4>(arr); }
|
||||
template<typename T, size_t N> Serializer& array8(const std::array<T, N> &arr) { return array<8>(arr); }
|
||||
template<typename T, typename Ext>
|
||||
Serializer &extension1(const T &v, Ext &&ext) {
|
||||
return extension<1>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, size_t N> Serializer& array1(const T (&arr)[N]) { return array<1>(arr); }
|
||||
template<typename T, size_t N> Serializer& array2(const T (&arr)[N]) { return array<2>(arr); }
|
||||
template<typename T, size_t N> Serializer& array4(const T (&arr)[N]) { return array<4>(arr); }
|
||||
template<typename T, size_t N> Serializer& array8(const T (&arr)[N]) { return array<8>(arr); }
|
||||
template<typename T, typename Ext>
|
||||
Serializer &extension2(const T &v, Ext &&ext) {
|
||||
return extension<2>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Serializer &extension4(const T &v, Ext &&ext) {
|
||||
return extension<4>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, typename Ext>
|
||||
Serializer &extension8(const T &v, Ext &&ext) {
|
||||
return extension<8>(v, std::forward<Ext>(ext));
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &substitution1(const T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<1>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &substitution2(const T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<2>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &substitution4(const T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<4>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &substitution8(const T &v, const std::array<T, N> &expectedValues) {
|
||||
return substitution<8>(v, expectedValues);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Serializer &text1(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<1>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer &text2(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<2>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer &text4(const std::basic_string<T> &str, size_t maxSize) {
|
||||
return text<4>(str, maxSize);
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &text1(const T (&str)[N]) { return text<1>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &text2(const T (&str)[N]) { return text<2>(str); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &text4(const T (&str)[N]) { return text<4>(str); }
|
||||
|
||||
template<typename T>
|
||||
Serializer &container1(T &&obj, size_t maxSize) {
|
||||
return container<1>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer &container2(T &&obj, size_t maxSize) {
|
||||
return container<2>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer &container4(T &&obj, size_t maxSize) {
|
||||
return container<4>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serializer &container8(T &&obj, size_t maxSize) {
|
||||
return container<8>(std::forward<T>(obj), maxSize);
|
||||
}
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array1(const std::array<T, N> &arr) { return array<1>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array2(const std::array<T, N> &arr) { return array<2>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array4(const std::array<T, N> &arr) { return array<4>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array8(const std::array<T, N> &arr) { return array<8>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array1(const T (&arr)[N]) { return array<1>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array2(const T (&arr)[N]) { return array<2>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array4(const T (&arr)[N]) { return array<4>(arr); }
|
||||
|
||||
template<typename T, size_t N>
|
||||
Serializer &array8(const T (&arr)[N]) { return array<8>(arr); }
|
||||
|
||||
private:
|
||||
Writter &_writter;
|
||||
@@ -287,11 +397,11 @@ namespace bitsery {
|
||||
_writter.writeBits(1u, 1);
|
||||
_writter.writeBits(size, 7);
|
||||
} else if (size < 0x4000u) {
|
||||
_writter.writeBits(2u,2);
|
||||
_writter.writeBits(2u, 2);
|
||||
_writter.writeBits(size, 14);
|
||||
} else {
|
||||
assert(size < 0x40000000u);
|
||||
_writter.writeBits(0u,2);
|
||||
_writter.writeBits(0u, 2);
|
||||
_writter.writeBits(size, 30);
|
||||
}
|
||||
}
|
||||
@@ -300,7 +410,7 @@ namespace bitsery {
|
||||
//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)
|
||||
for (; first != last; ++first)
|
||||
value<VSIZE>(*first);
|
||||
};
|
||||
|
||||
@@ -315,14 +425,14 @@ namespace bitsery {
|
||||
//process by calling functions
|
||||
template<typename It, typename Fnc>
|
||||
void procContainer(It first, It last, Fnc fnc) {
|
||||
for (;first != last; ++first)
|
||||
for (; first != last; ++first)
|
||||
fnc(*this, *first);
|
||||
};
|
||||
|
||||
//process object types
|
||||
template<typename It>
|
||||
void procContainer(It first, It last) {
|
||||
for (;first != last; ++first)
|
||||
for (; first != last; ++first)
|
||||
object(*first);
|
||||
};
|
||||
|
||||
|
||||
@@ -36,24 +36,20 @@
|
||||
#include <bitsery/ext/optional.h>
|
||||
|
||||
|
||||
template <typename T>
|
||||
using extoptional = bitsery::ext::optional<T>;
|
||||
using extoptional = bitsery::ext::optional;
|
||||
|
||||
using testing::Eq;
|
||||
|
||||
|
||||
template <typename T>
|
||||
void test(SerializationContext& ctx, const T& v, T& r) {
|
||||
auto fnc = [](auto ser, auto& v) {
|
||||
ser.template value<sizeof(v)>(v);
|
||||
};
|
||||
ctx.createSerializer().ext<extoptional>(v, fnc);
|
||||
ctx.createDeserializer().ext<extoptional>(r, fnc);
|
||||
ctx.createSerializer().extension4(v, extoptional{});
|
||||
ctx.createDeserializer().extension4(r, extoptional{});
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionOptional, EmptyOptional) {
|
||||
std::optional<int> t1{};
|
||||
std::optional<int> r1{};
|
||||
std::optional<int32_t> t1{};
|
||||
std::optional<int32_t> r1{};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
@@ -69,8 +65,8 @@ TEST(SerializeExtensionOptional, EmptyOptional) {
|
||||
}
|
||||
|
||||
TEST(SerializeExtensionOptional, OptionalHasValue) {
|
||||
std::optional<int> t1{43};
|
||||
std::optional<int> r1{52};
|
||||
std::optional<int32_t> t1{43};
|
||||
std::optional<int32_t> r1{52};
|
||||
|
||||
SerializationContext ctx1;
|
||||
test(ctx1,t1, r1);
|
||||
|
||||
Reference in New Issue
Block a user