buffer writer improvements

This commit is contained in:
fraillt
2017-08-25 14:11:20 +03:00
parent 5d192f2002
commit 944197db97
18 changed files with 312 additions and 298 deletions

View File

@@ -3,12 +3,13 @@
### Features
* now all serializer/deserializer functions return void, to avoid undefined behaviour for functions parameters evaluation when using method chaining. There was no benefits apart from *nicer* syntax, but could have undefined behaviour when building complex serialization flows.
* changed BufferWriter/Reader config, added *FixedBufferSize* bool parameter for *BufferWriter* for much better serializer performance (more than 50% improvement). Default is old behavour - using back_insert_iterator when writing.
* 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.
* user friendly 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.1"></a>
# [2.0.1](https://github.com/fraillt/bitsery/compare/v2.0.0...v2.0.1) (2017-08-12)

View File

@@ -135,4 +135,4 @@ serialize(des, res); //des-> Deserializer, data-> Player
You have learned how to write *serialize* function for your type, that works with serialization and deserialization. You also learned that deserialization is very similar to serialization, but has runtime error checking.
In [next chapter](composition.md) you'll learn more serialization/deserialization functions and how to compose them efficiently to add default or custom serialization/deserialization behaviour.
In [next chapter](composition.md) you'll learn how to compose complex serialization/deserialization flows efficiently.

View File

@@ -24,7 +24,6 @@ SERIALIZE(Player) {
s.value4(o.pos.y);
s.value4(o.pos.z);
s.text1(o.name);
return s;
}
Player createData() {

View File

@@ -26,16 +26,15 @@
#define BITSERY_BUFFER_READER_H
#include "common.h"
#include <algorithm>
#include <vector>
namespace bitsery {
template<typename Config>
struct BasicBufferReader {
using ValueType = typename Config::BufferValueType;
using ScratchType = typename Config::BufferScrathType;
using BufferType = typename Config::BufferType;
using ValueType = typename BufferType::value_type;
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
BasicBufferReader(const ValueType *data, size_t size) : _pos{data}, _end{data + size} {
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
@@ -45,11 +44,10 @@ namespace bitsery {
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
}
explicit BasicBufferReader(const std::vector<ValueType> &buf) : BasicBufferReader(buf.data(), buf.size()) {
}
BasicBufferReader(typename BufferType::iterator begin, typename BufferType::iterator end)
: BasicBufferReader(std::addressof(*begin), std::distance(begin, end)) {}
template<size_t N>
explicit BasicBufferReader(const ValueType (&data)[N]): BasicBufferReader(data, N) {
explicit BasicBufferReader(const BufferType &buf) : BasicBufferReader(buf.data(), buf.size()) {
}
BasicBufferReader(const BasicBufferReader &) = delete;

View File

@@ -27,10 +27,7 @@
#include "common.h"
#include <cassert>
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
namespace bitsery {
@@ -62,11 +59,11 @@ namespace bitsery {
}
void flush() {
_bitsCount += (8 - (_bitsCount % 8)) % 8;
}
//get size in bytes
size_t getSize() const {
size_t getWrittenBytesCount() const {
return _bitsCount / 8;
}
@@ -77,11 +74,13 @@ namespace bitsery {
template<typename Config>
struct BasicBufferWriter {
using ValueType = typename Config::BufferValueType;
using ScratchType = typename Config::BufferScrathType;
using BufferType = typename Config::BufferType;
using ValueType = typename BufferType::value_type;
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
using BufferContext = details::WriteBufferContext<BufferType, Config::FixedBufferSize>;
explicit BasicBufferWriter(std::vector<ValueType> &buffer) : _outIt{std::back_inserter(buffer)} {
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
explicit BasicBufferWriter(BufferType &buffer) : _bufferContext{buffer} {
static_assert(std::is_unsigned<ValueType>(), "Config::BufferType::value_type 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");
@@ -148,6 +147,9 @@ namespace bitsery {
}
}
size_t getWrittenBytesCount() const {
return _bufferContext.getWrittenBytesCount();
}
private:
@@ -161,13 +163,13 @@ namespace bitsery {
void _directWriteSwapTag(const T *v, size_t count, std::true_type) {
std::for_each(v, std::next(v, count), [this](const T &v) {
const auto res = details::swap(v);
std::copy_n(reinterpret_cast<const ValueType *>(&res), sizeof(T), _outIt);
_bufferContext.write(reinterpret_cast<const ValueType *>(&res), sizeof(T));
});
}
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);
_bufferContext.write(reinterpret_cast<const ValueType *>(v), count * sizeof(T));
}
template<typename T>
@@ -207,7 +209,7 @@ namespace bitsery {
const ValueType _MASK = std::numeric_limits<ValueType>::max();
std::back_insert_iterator<std::vector<ValueType>> _outIt;
BufferContext _bufferContext;
ScratchType _scratch{};
size_t _scratchBits{};
};

View File

@@ -25,13 +25,14 @@
#define BITSERY_COMMON_H
#include "details/buffer_common.h"
#include <vector>
namespace bitsery {
struct DefaultConfig {
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
using BufferValueType = uint8_t;
using BufferScrathType = uint16_t;
static constexpr bool FixedBufferSize = false;//false means that buffer is resizable and will be used back_insert_iterator for insertion, for reading has no effect.
using BufferType = std::vector<uint8_t>;//buffer value type must be unsigned, currently only uint8_t supported
};
/*
@@ -40,11 +41,11 @@ namespace bitsery {
#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)
void 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)
friend void serialize(S& s, T& o)
}

View File

@@ -56,7 +56,7 @@ namespace bitsery {
template<typename T>
DeltaDeserializer &object(T &&obj) {
if (getChangedState(obj))
return serialize(*this, std::forward<T>(obj));
serialize(*this, std::forward<T>(obj));
return *this;
}

View File

@@ -42,8 +42,9 @@ namespace bitsery {
*/
template<typename T>
Deserializer &object(T &&obj) {
return details::SerializeFunction<Deserializer, T>::invoke(*this, std::forward<T>(obj));
void object(T &&obj) {
if (_isValid)
details::SerializeFunction<Deserializer, T>::invoke(*this, std::forward<T>(obj));
}
/*
@@ -51,29 +52,23 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
Deserializer &value(T &v) {
void value(T &v) {
static_assert(std::numeric_limits<T>::is_iec559, "");
if (_isValid) {
if (_isValid)
_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) {
void value(T &v) {
using UT = std::underlying_type_t<T>;
if (_isValid) {
if (_isValid)
_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) {
if (_isValid) {
void value(T &v) {
if (_isValid)
_isValid = _reader.template readBytes<VSIZE>(v);
}
return *this;
}
/*
@@ -81,9 +76,9 @@ namespace bitsery {
*/
template<typename T, typename Fnc>
Deserializer &custom(T &&obj, Fnc &&fnc) {
fnc(*this, std::forward<T>(obj));
return *this;
void custom(T &&obj, Fnc &&fnc) {
if (_isValid)
fnc(*this, std::forward<T>(obj));
};
/*
@@ -91,37 +86,36 @@ namespace bitsery {
*/
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;
void extension(T &obj, Ext &&ext, Fnc &&fnc) {
if (_isValid)
ext.deserialize(obj, *this, std::forward<Fnc>(fnc));
};
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;
void extension(T &obj, Ext &&ext) {
if (_isValid)
ext.deserialize(obj, *this, [](auto &s, auto &v) { s.template value<VSIZE>(v); });
};
template<typename T, typename Ext>
Deserializer &extension(T &obj, Ext &&ext) {
ext.deserialize(obj, *this, [](auto &s, auto &v) { s.object(v); });
return *this;
void extension(T &obj, Ext &&ext) {
if (_isValid)
ext.deserialize(obj, *this, [](auto &s, auto &v) { s.object(v); });
};
/*
* bool
*/
Deserializer &boolBit(bool &v) {
void boolBit(bool &v) {
if (_isValid) {
unsigned char tmp;
_isValid = _reader.readBits(tmp, 1);
v = tmp == 1;
}
return *this;
}
Deserializer &boolByte(bool &v) {
void boolByte(bool &v) {
if (_isValid) {
unsigned char tmp;
_isValid = _reader.template readBytes<1>(tmp);
@@ -129,7 +123,6 @@ namespace bitsery {
_isValid = tmp < 2;
v = tmp == 1;
}
return *this;
}
/*
@@ -137,21 +130,20 @@ namespace bitsery {
*/
template<typename T>
Deserializer &range(T &v, const RangeSpec <T> &range) {
void range(T &v, const RangeSpec<T> &range) {
if (_isValid) {
_isValid = _reader.readBits(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v), range.bitsRequired);
details::setRangeValue(v, range);
if (_isValid)
_isValid = details::isRangeValid(v, range);
}
return *this;
}
/*
* substitution overloads
*/
template<typename T, size_t N, typename Fnc>
Deserializer &substitution(T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
void substitution(T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
@@ -160,11 +152,10 @@ namespace bitsery {
else
fnc(*this, v);
}
return *this;
};
template<size_t VSIZE, typename T, size_t N>
Deserializer &substitution(T &v, const std::array<T, N> &expectedValues) {
void substitution(T &v, const std::array<T, N> &expectedValues) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
@@ -173,11 +164,10 @@ namespace bitsery {
else
value<VSIZE>(v);
}
return *this;
};
template<typename T, size_t N>
Deserializer &substitution(T &v, const std::array<T, N> &expectedValues) {
void substitution(T &v, const std::array<T, N> &expectedValues) {
size_t index;
range(index, {{}, N + 1});
if (_isValid) {
@@ -186,7 +176,6 @@ namespace bitsery {
else
object(v);
}
return *this;
};
/*
@@ -194,18 +183,17 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
Deserializer &text(std::basic_string<T> &str, size_t maxSize) {
void text(std::basic_string<T> &str, size_t maxSize) {
size_t size;
readSize(size, maxSize);
if (_isValid) {
str.resize(size);
procContainer<VSIZE>(std::begin(str), std::end(str), std::true_type{});
}
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Deserializer &text(T (&str)[N]) {
void text(T (&str)[N]) {
size_t size;
readSize(size, N - 1);
if (_isValid) {
@@ -214,7 +202,6 @@ namespace bitsery {
//null-terminated string
str[size] = {};
}
return *this;
}
/*
@@ -222,36 +209,33 @@ namespace bitsery {
*/
template<typename T, typename Fnc>
Deserializer &container(T &&obj, size_t maxSize, Fnc &&fnc) {
void container(T &&obj, size_t maxSize, Fnc &&fnc) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
procContainer(std::begin(obj), std::end(obj), std::forward<Fnc>(fnc));
}
return *this;
}
template<size_t VSIZE, typename T>
Deserializer &container(T &obj, size_t maxSize) {
void container(T &obj, size_t maxSize) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
}
return *this;
}
template<typename T>
Deserializer &container(T &obj, size_t maxSize) {
void container(T &obj, size_t maxSize) {
decltype(obj.size()) size{};
readSize(size, maxSize);
if (_isValid) {
obj.resize(size);
procContainer(std::begin(obj), std::end(obj));
}
return *this;
}
/*
@@ -261,46 +245,39 @@ namespace bitsery {
//std::array overloads
template<typename T, size_t N, typename Fnc>
Deserializer &array(std::array<T, N> &arr, Fnc &&fnc) {
void 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) {
void 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) {
void array(std::array<T, N> &arr) {
procContainer(std::begin(arr), std::end(arr));
return *this;
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
Deserializer &array(T (&arr)[N], Fnc &&fnc) {
void 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]) {
void 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]) {
void array(T (&arr)[N]) {
procContainer(std::begin(arr), std::end(arr));
return *this;
}
Deserializer &align() {
void align() {
_reader.align();
return *this;
}
bool isValid() const {
@@ -310,100 +287,94 @@ namespace bitsery {
//overloads for functions with explicit type size
template<typename T>
Deserializer &value1(T &&v) { return value<1>(std::forward<T>(v)); }
void value1(T &&v) { value<1>(std::forward<T>(v)); }
template<typename T>
Deserializer &value2(T &&v) { return value<2>(std::forward<T>(v)); }
void value2(T &&v) { value<2>(std::forward<T>(v)); }
template<typename T>
Deserializer &value4(T &&v) { return value<4>(std::forward<T>(v)); }
void value4(T &&v) { value<4>(std::forward<T>(v)); }
template<typename T>
Deserializer &value8(T &&v) { return value<8>(std::forward<T>(v)); }
void value8(T &&v) { 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));
};
void extension1(T &v, Ext &&ext) { 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));
};
void extension2(T &v, Ext &&ext) { 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));
};
void extension4(T &v, Ext &&ext) { 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));
};
void extension8(T &v, Ext &&ext) { 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);
};
void substitution1(T &v, const std::array<T, N> &expectedValues) { 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);
};
void substitution2(T &v, const std::array<T, N> &expectedValues) { 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);
};
void substitution4(T &v, const std::array<T, N> &expectedValues) { 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);
};
void substitution8(T &v, const std::array<T, N> &expectedValues) { substitution<8>(v, expectedValues); };
template<typename T>
Deserializer &text1(std::basic_string<T> &str, size_t maxSize) {
return text<1>(str, maxSize);
}
void text1(std::basic_string<T> &str, size_t maxSize) { text<1>(str, maxSize); }
template<typename T>
Deserializer &text2(std::basic_string<T> &str, size_t maxSize) {
return text<2>(str, maxSize);
}
void text2(std::basic_string<T> &str, size_t maxSize) { text<2>(str, maxSize); }
template<typename T>
Deserializer &text4(std::basic_string<T> &str, size_t maxSize) {
return text<4>(str, maxSize);
}
void text4(std::basic_string<T> &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T, size_t N>
Deserializer &text1(T (&str)[N]) { return text<1>(str); }
void text1(T (&str)[N]) { text<1>(str); }
template<typename T, size_t N>
Deserializer &text2(T (&str)[N]) { return text<2>(str); }
void text2(T (&str)[N]) { text<2>(str); }
template<typename T, size_t N>
Deserializer &text4(T (&str)[N]) { return text<4>(str); }
void text4(T (&str)[N]) { text<4>(str); }
template<typename T>
Deserializer &container1(T &&obj, size_t maxSize) {
return container<1>(std::forward<T>(obj), maxSize);
}
void container1(T &&obj, size_t maxSize) { 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);
}
void container2(T &&obj, size_t maxSize) { 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);
}
void container4(T &&obj, size_t maxSize) { 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);
}
void container8(T &&obj, size_t maxSize) { container<8>(std::forward<T>(obj), maxSize); }
template<typename T, size_t N>
void array1(std::array<T, N> &arr) { array<1>(arr); }
template<typename T, size_t N>
void array2(std::array<T, N> &arr) { array<2>(arr); }
template<typename T, size_t N>
void array4(std::array<T, N> &arr) { array<4>(arr); }
template<typename T, size_t N>
void array8(std::array<T, N> &arr) { array<8>(arr); }
template<typename T, size_t N>
void array1(T (&arr)[N]) { array<1>(arr); }
template<typename T, size_t N>
void array2(T (&arr)[N]) { array<2>(arr); }
template<typename T, size_t N>
void array4(T (&arr)[N]) { array<4>(arr); }
template<typename T, size_t N>
void array8(T (&arr)[N]) { array<8>(arr); }
private:
Reader &_reader;

View File

@@ -26,6 +26,7 @@
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <algorithm>
namespace bitsery {
@@ -93,6 +94,80 @@ namespace bitsery {
: EndiannessType::BigEndian;
}
template<typename T>
struct SCRATCH_TYPE {
};
template<>
struct SCRATCH_TYPE<uint8_t> {
using type = uint16_t;
};
template<>
struct SCRATCH_TYPE<uint16_t> {
using type = uint32_t;
};
template<>
struct SCRATCH_TYPE<uint32_t> {
using type = uint64_t;
};
template<typename Buffer, bool isFixed>
class WriteBufferContext {
};
template<typename Buffer>
class WriteBufferContext<Buffer, true> {
public:
using ValueType = typename Buffer::value_type;
using IteratorType = typename Buffer::iterator;
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer},
_outIt{buffer.begin()} {
}
void write(const ValueType *data, size_t size) {
_outIt = std::copy_n(data, size, _outIt);
}
size_t getWrittenBytesCount() const {
return std::distance(_buffer.begin(), _outIt) * sizeof(ValueType);
}
private:
Buffer &_buffer;
IteratorType _outIt;
};
template<typename Buffer>
class WriteBufferContext<Buffer, false> {
public:
using ValueType = typename Buffer::value_type;
using IteratorType = std::back_insert_iterator<Buffer>;
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer},
_outIt{std::back_insert_iterator<Buffer>(buffer)},
_initialSize{buffer.size()} {
}
void write(const ValueType *data, size_t size) {
std::copy_n(data, size, _outIt);
}
size_t getWrittenBytesCount() const {
return (std::distance(_buffer.begin(), _buffer.end()) - _initialSize) * sizeof(ValueType);
}
private:
Buffer &_buffer;
IteratorType _outIt;
size_t _initialSize;
};
}
}

View File

@@ -201,18 +201,17 @@ namespace bitsery {
template<typename S, typename T, typename Enabled = void>
struct SerializeFunction {
static S &invoke(S &s, T &v) {
static void 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
std::is_same<void, decltype(serialize(std::declval<S &>(), std::declval<T &>()))>::value
>::type> {
static S &invoke(S &s, T &v) {
return serialize(s, v);
static void invoke(S &s, T &v) {
serialize(s, v);
}
};

View File

@@ -42,8 +42,8 @@ namespace bitsery {
* object function
*/
template<typename T>
Serializer &object(T &&obj) {
return details::SerializeFunction<Serializer, T>::invoke(*this, std::forward<T>(obj));
void object(T &&obj) {
details::SerializeFunction<Serializer, T>::invoke(*this, std::forward<T>(obj));
}
/*
@@ -51,22 +51,19 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
Serializer &value(const T &v) {
void value(const T &v) {
static_assert(std::numeric_limits<T>::is_iec559, "");
_writter.template writeBytes<VSIZE>(reinterpret_cast<const details::SAME_SIZE_UNSIGNED<T> &>(v));
return *this;
}
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
Serializer &value(const T &v) {
void 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) {
void value(const T &v) {
_writter.template writeBytes<VSIZE>(v);
return *this;
}
/*
@@ -74,9 +71,8 @@ namespace bitsery {
*/
template<typename T, typename Fnc>
Serializer &custom(T &&obj, Fnc &&fnc) {
void custom(T &&obj, Fnc &&fnc) {
fnc(*this, std::forward<T>(obj));
return *this;
};
/*
@@ -84,35 +80,32 @@ namespace bitsery {
*/
template<typename T, typename Ext, typename Fnc>
Serializer &extension(const T &obj, Ext &&ext, Fnc &&fnc) {
void 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) {
void 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) {
void extension(const T &obj, Ext &&ext) {
ext.serialize(obj, *this, [](auto &s, auto &v) { s.object(v); });
return *this;
};
/*
* bool
*/
Serializer &boolBit(bool v) {
void boolBit(bool v) {
_writter.writeBits(static_cast<unsigned char>(v ? 1 : 0), 1);
return *this;
}
Serializer &boolByte(bool v) {
void boolByte(bool v) {
_writter.template writeBytes<1>(static_cast<unsigned char>(v ? 1 : 0));
return *this;
}
/*
@@ -120,41 +113,37 @@ namespace bitsery {
*/
template<typename T>
Serializer &range(const T &v, const RangeSpec<T> &range) {
void 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);
return *this;
using BT = decltype(details::getRangeValue(v, range));
_writter.template writeBits<BT>(details::getRangeValue(v, range), range.bitsRequired);
}
/*
* substitution overloads
*/
template<typename T, size_t N, typename Fnc>
Serializer &substitution(const T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
void substitution(const T &v, const std::array<T, N> &expectedValues, Fnc &&fnc) {
auto index = details::findSubstitutionIndex(v, expectedValues);
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) {
void substitution(const T &v, const std::array<T, N> &expectedValues) {
auto index = details::findSubstitutionIndex(v, expectedValues);
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) {
void substitution(const T &v, const std::array<T, N> &expectedValues) {
auto index = details::findSubstitutionIndex(v, expectedValues);
range(index, {{}, N + 1});
if (!index)
object(v);
return *this;
};
/*
@@ -162,22 +151,20 @@ namespace bitsery {
*/
template<size_t VSIZE, typename T>
Serializer &text(const std::basic_string<T> &str, size_t maxSize) {
void text(const std::basic_string<T> &str, size_t maxSize) {
assert(str.size() <= maxSize);
auto first = std::begin(str);
auto last = std::end(str);
writeSize(std::distance(first, last));
procContainer<VSIZE>(first, last, std::true_type{});
return *this;
}
template<size_t VSIZE, typename T, size_t N>
Serializer &text(const T (&str)[N]) {
void 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));
procContainer<VSIZE>(first, last, std::true_type{});
return *this;
}
/*
@@ -185,29 +172,26 @@ namespace bitsery {
*/
template<typename T, typename Fnc>
Serializer &container(const T &obj, size_t maxSize, Fnc &&fnc) {
void 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));
return *this;
}
template<size_t VSIZE, typename T>
Serializer &container(const T &obj, size_t maxSize) {
void container(const T &obj, size_t maxSize) {
static_assert(VSIZE > 0, "");
assert(obj.size() <= maxSize);
writeSize(obj.size());
//todo optimisation is possible for contigous containers, but currently there is no compile-time check for this
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
return *this;
}
template<typename T>
Serializer &container(const T &obj, size_t maxSize) {
void container(const T &obj, size_t maxSize) {
assert(obj.size() <= maxSize);
writeSize(obj.size());
procContainer(std::begin(obj), std::end(obj));
return *this;
}
/*
@@ -217,48 +201,41 @@ namespace bitsery {
//std::array overloads
template<typename T, size_t N, typename Fnc>
Serializer &array(const std::array<T, N> &arr, Fnc &&fnc) {
void 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) {
void 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) {
void array(const std::array<T, N> &arr) {
procContainer(std::begin(arr), std::end(arr));
return *this;
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
Serializer &array(const T (&arr)[N], Fnc &&fnc) {
void 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]) {
void 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]) {
void array(const T (&arr)[N]) {
procContainer(std::begin(arr), std::end(arr));
return *this;
}
Serializer &align() {
void align() {
_writter.align();
return *this;
}
bool isValid() const {
@@ -270,124 +247,102 @@ namespace bitsery {
//overloads for functions with explicit type size
template<typename T>
Serializer &value1(T &&v) { return value<1>(std::forward<T>(v)); }
void value1(T &&v) { value<1>(std::forward<T>(v)); }
template<typename T>
Serializer &value2(T &&v) { return value<2>(std::forward<T>(v)); }
void value2(T &&v) { value<2>(std::forward<T>(v)); }
template<typename T>
Serializer &value4(T &&v) { return value<4>(std::forward<T>(v)); }
void value4(T &&v) { value<4>(std::forward<T>(v)); }
template<typename T>
Serializer &value8(T &&v) { return value<8>(std::forward<T>(v)); }
void value8(T &&v) { value<8>(std::forward<T>(v)); }
template<typename T, typename Ext>
Serializer &extension1(const T &v, Ext &&ext) {
return extension<1>(v, std::forward<Ext>(ext));
};
void extension1(const T &v, Ext &&ext) { extension<1>(v, std::forward<Ext>(ext)); };
template<typename T, typename Ext>
Serializer &extension2(const T &v, Ext &&ext) {
return extension<2>(v, std::forward<Ext>(ext));
};
void extension2(const T &v, Ext &&ext) { 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));
};
void extension4(const T &v, Ext &&ext) { 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));
void extension8(const T &v, Ext &&ext) { extension<8>(v, std::forward<Ext>(ext)); };
template<typename T, size_t N>
void substitution1(const T &v, const std::array<T, N> &expectedValues) {
substitution<1>(v, expectedValues);
};
template<typename T, size_t N>
Serializer &substitution1(const T &v, const std::array<T, N> &expectedValues) {
return substitution<1>(v, expectedValues);
void substitution2(const T &v, const std::array<T, N> &expectedValues) {
substitution<2>(v, expectedValues);
};
template<typename T, size_t N>
Serializer &substitution2(const T &v, const std::array<T, N> &expectedValues) {
return substitution<2>(v, expectedValues);
void substitution4(const T &v, const std::array<T, N> &expectedValues) {
substitution<4>(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);
void substitution8(const T &v, const std::array<T, N> &expectedValues) {
substitution<8>(v, expectedValues);
};
template<typename T>
Serializer &text1(const std::basic_string<T> &str, size_t maxSize) {
return text<1>(str, maxSize);
}
void text1(const std::basic_string<T> &str, size_t maxSize) { text<1>(str, maxSize); }
template<typename T>
Serializer &text2(const std::basic_string<T> &str, size_t maxSize) {
return text<2>(str, maxSize);
}
void text2(const std::basic_string<T> &str, size_t maxSize) { text<2>(str, maxSize); }
template<typename T>
Serializer &text4(const std::basic_string<T> &str, size_t maxSize) {
return text<4>(str, maxSize);
}
void text4(const std::basic_string<T> &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T, size_t N>
Serializer &text1(const T (&str)[N]) { return text<1>(str); }
void text1(const T (&str)[N]) { text<1>(str); }
template<typename T, size_t N>
Serializer &text2(const T (&str)[N]) { return text<2>(str); }
void text2(const T (&str)[N]) { text<2>(str); }
template<typename T, size_t N>
Serializer &text4(const T (&str)[N]) { return text<4>(str); }
void text4(const T (&str)[N]) { text<4>(str); }
template<typename T>
Serializer &container1(T &&obj, size_t maxSize) {
return container<1>(std::forward<T>(obj), maxSize);
}
void container1(T &&obj, size_t maxSize) { 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);
}
void container2(T &&obj, size_t maxSize) { 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);
}
void container4(T &&obj, size_t maxSize) { 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);
}
void container8(T &&obj, size_t maxSize) { 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); }
void array1(const std::array<T, N> &arr) { array<1>(arr); }
template<typename T, size_t N>
Serializer &array2(const std::array<T, N> &arr) { return array<2>(arr); }
void array2(const std::array<T, N> &arr) { array<2>(arr); }
template<typename T, size_t N>
Serializer &array4(const std::array<T, N> &arr) { return array<4>(arr); }
void array4(const std::array<T, N> &arr) { array<4>(arr); }
template<typename T, size_t N>
Serializer &array8(const std::array<T, N> &arr) { return array<8>(arr); }
void array8(const std::array<T, N> &arr) { array<8>(arr); }
template<typename T, size_t N>
Serializer &array1(const T (&arr)[N]) { return array<1>(arr); }
void array1(const T (&arr)[N]) { array<1>(arr); }
template<typename T, size_t N>
Serializer &array2(const T (&arr)[N]) { return array<2>(arr); }
void array2(const T (&arr)[N]) { array<2>(arr); }
template<typename T, size_t N>
Serializer &array4(const T (&arr)[N]) { return array<4>(arr); }
void array4(const T (&arr)[N]) { array<4>(arr); }
template<typename T, size_t N>
Serializer &array8(const T (&arr)[N]) { return array<8>(arr); }
void array8(const T (&arr)[N]) { array<8>(arr); }
private:
Writter &_writter;
@@ -418,8 +373,7 @@ namespace bitsery {
//true_type means, that we can copy whole buffer
template<size_t VSIZE, typename It>
void procContainer(It first, It last, std::true_type) {
if (first != last)
_writter.template writeBuffer<VSIZE>(&(*first), std::distance(first, last));
_writter.template writeBuffer<VSIZE>(&(*first), std::distance(first, last));
};
//process by calling functions

View File

@@ -30,7 +30,7 @@ using testing::Eq;
using testing::ContainerEq;
using bitsery::BufferWriter;
using bitsery::BufferReader;
using Buffer = std::vector<bitsery::DefaultConfig::BufferValueType>;
using Buffer = bitsery::DefaultConfig::BufferType;
struct IntegralUnsignedTypes {
uint32_t a;

View File

@@ -31,7 +31,7 @@ using testing::Eq;
using testing::ContainerEq;
using bitsery::BufferWriter;
using bitsery::BufferReader;
using Buffer = std::vector<bitsery::DefaultConfig::BufferValueType>;
using Buffer = bitsery::DefaultConfig::BufferType;
struct IntegralTypes {
int64_t a;
@@ -124,7 +124,7 @@ TEST(BufferBytesOperations, BufferReaderUsingDataPlusSizeCtor) {
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(BufferBytesOperations, BufferReaderUsingCArrayCtor) {
TEST(BufferBytesOperations, BufferReaderUsingIteratorsCtor) {
//setup data
auto data =getInitializedIntegralTypes();
//create and write to buffer
@@ -133,10 +133,8 @@ TEST(BufferBytesOperations, BufferReaderUsingCArrayCtor) {
writeIntegralTypesToBuffer(bw, data);
ASSERT_THAT(std::distance(buf.begin(), buf.end()), Eq(18));
uint8_t cArrBuf[18];
std::copy(buf.begin(), buf.end(), cArrBuf);
//read from buffer
BufferReader br{cArrBuf};
BufferReader br{buf.begin(), buf.end()};
IntegralTypes res{};
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[0]), Eq(true));

View File

@@ -30,7 +30,7 @@ using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;
using bitsery::DefaultConfig;
using Buffer = std::vector<bitsery::DefaultConfig::BufferValueType>;
using Buffer = bitsery::DefaultConfig::BufferType;
constexpr EndiannessType getInverseEndianness(EndiannessType e) {
return e == EndiannessType::LittleEndian
@@ -40,8 +40,8 @@ constexpr EndiannessType getInverseEndianness(EndiannessType e) {
struct InverseEndiannessConfig {
static constexpr bitsery::EndiannessType NetworkEndianness = getInverseEndianness(DefaultConfig::NetworkEndianness);
using BufferValueType = DefaultConfig::BufferValueType;
using BufferScrathType = DefaultConfig::BufferScrathType;
static constexpr bool FixedBufferSize = DefaultConfig::FixedBufferSize;
using BufferType = DefaultConfig::BufferType;
};
struct IntegralTypes {
@@ -147,7 +147,7 @@ struct IntegralUnsignedTypes {
TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
//fill initial values
static_assert(sizeof(DefaultConfig::BufferValueType) == 1, "currently only 1 byte size, value size is supported");
static_assert(sizeof(DefaultConfig::BufferType::value_type) == 1, "currently only 1 byte size, value size is supported");
//fill initial values
constexpr IntegralUnsignedTypes src {
0x0000334455667788,//bits 19

View File

@@ -33,8 +33,12 @@ TEST(SerializeBooleans, BoolAsBit) {
bool t2{false};
bool res1;
bool res2;
ctx.createSerializer().boolBit(t1).boolBit(t2);
ctx.createDeserializer().boolBit(res1).boolBit(res2);
auto ser = ctx.createSerializer();
ser.boolBit(t1);
ser.boolBit(t2);
auto des = ctx.createDeserializer();
des.boolBit(res1);
des.boolBit(res2);
EXPECT_THAT(res1, Eq(t1));
EXPECT_THAT(res2, Eq(t2));
@@ -47,8 +51,12 @@ TEST(SerializeBooleans, BoolAsByte) {
bool t2{false};
bool res1;
bool res2;
ctx.createSerializer().boolByte(t1).boolByte(t2);
ctx.createDeserializer().boolByte(res1).boolByte(res2);
auto ser = ctx.createSerializer();
ser.boolByte(t1);
ser.boolByte(t2);
auto des = ctx.createDeserializer();
des.boolByte(res1);
des.boolByte(res2);
EXPECT_THAT(res1, Eq(t1));
EXPECT_THAT(res2, Eq(t2));

View File

@@ -67,12 +67,14 @@ TEST(SerializeFSArrayStdArray, CustomFunctionThatSerializesAnEmptyByteEveryEleme
auto ser = ctx.createSerializer();
ser.array(src, [](auto &s, auto& v) {
char tmp{};
s.object(v).value1(tmp);
s.object(v);
s.value1(tmp);
});
auto des = ctx.createDeserializer();
des.array(res, [](auto &s, auto& v) {
char tmp{};
s.object(v).value1(tmp);
s.object(v);
s.value1(tmp);
});
EXPECT_THAT(ctx.getBufferSize(), Eq(src.size() * (MyStruct1::SIZE + sizeof(char))));
@@ -118,12 +120,14 @@ TEST(SerializeFSArrayCArray, CustomFunctionThatSerializesAnEmptyByteEveryElement
auto ser = ctx.createSerializer();
ser.array(src, [](auto& s, auto& v) {
char tmp{};
s.object(v).value1(tmp);
s.object(v);
s.value1(tmp);
});
auto des = ctx.createDeserializer();
des.array(res, [](auto& s, auto& v) {
char tmp{};
s.object(v).value1(tmp);
s.object(v);
s.value1(tmp);
});
EXPECT_THAT(ctx.getBufferSize(), Eq(std::extent<decltype(src)>::value * (MyStruct1::SIZE + sizeof(char))));

View File

@@ -60,13 +60,12 @@ SERIALIZE(Z)
{
s.object(o.x);
s.object(o.y);
return s;
}
SERIALIZE(X)
{
return s.template value<sizeof(o.x)>(o.x)
.template text<1>(o.s, 1000);
s.template value<sizeof(o.x)>(o.x);
s.template text<1>(o.s, 1000);
}
SERIALIZE(Y)
@@ -77,7 +76,6 @@ SERIALIZE(Y)
s.array(o.arr, writeInt);
s.array(o.carr, writeInt);
s.container(o.vx, 10000, [](auto& s, auto& v) { s.object(v); });
return s;
}
@@ -157,7 +155,7 @@ TEST(DeltaSerializer, GeneralConceptTest) {
yNew.vx[1].s = "bla";
yNew.vx.push_back(X{ 3 });
std::vector<bitsery::DefaultConfig::BufferValueType> buf;
bitsery::DefaultConfig::BufferType buf;
bitsery::BufferWriter bw{ buf };
bitsery::DeltaSerializer<bitsery::BufferWriter, Y> ser(bw, y, yNew);
serialize(ser, yNew);

View File

@@ -33,20 +33,23 @@
*/
struct MyStruct1 {
MyStruct1(int v1, int v2):i1{v1}, i2{v2} {}
MyStruct1():MyStruct1{0,0} {}
MyStruct1(int v1, int v2) : i1{v1}, i2{v2} {}
MyStruct1() : MyStruct1{0, 0} {}
int i1;
int i2;
bool operator == (const MyStruct1& rhs) const {
bool operator==(const MyStruct1 &rhs) const {
return i1 == rhs.i1 && i2 == rhs.i2;
}
static constexpr size_t SIZE = sizeof(MyStruct1::i1) + sizeof(MyStruct1::i2);
};
SERIALIZE(MyStruct1) {
return s.
template value<sizeof(o.i1)>(o.i1).
template value<sizeof(o.i2)>(o.i2);
s.template value<sizeof(o.i1)>(o.i1);
s.template value<sizeof(o.i2)>(o.i2);
}
enum class MyEnumClass {
@@ -58,26 +61,28 @@ struct MyStruct2 {
V1, V2, V3, V4, V5, V6
};
MyStruct2(MyEnum e, MyStruct1 s):e1{e}, s1{s} {}
MyStruct2():MyStruct2{V1,{0,0}} {}
MyStruct2(MyEnum e, MyStruct1 s) : e1{e}, s1{s} {}
MyStruct2() : MyStruct2{V1, {0, 0}} {}
MyEnum e1;
MyStruct1 s1;
bool operator == (const MyStruct2& rhs) const {
bool operator==(const MyStruct2 &rhs) const {
return e1 == rhs.e1 && s1 == rhs.s1;
}
static constexpr size_t SIZE = MyStruct1::SIZE + sizeof(MyStruct2::e1);
};
SERIALIZE(MyStruct2) {
return s.
template value<sizeof(o.e1)>(o.e1).
object(o.s1);
s.template value<sizeof(o.e1)>(o.e1);
s.object(o.s1);
}
class SerializationContext {
std::vector<bitsery::DefaultConfig::BufferValueType> buf{};
bitsery::DefaultConfig::BufferType buf{};
std::unique_ptr<bitsery::BufferWriter> bw;
std::unique_ptr<bitsery::BufferReader> br;
public:
@@ -89,6 +94,7 @@ public:
size_t getBufferSize() const {
return buf.size();
}
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
//this function returns number of bytes writen to buffer, when reading/writing size of container
static size_t containerSizeSerializedBytesCount(size_t elemsCount) {