flexible syntax

This commit is contained in:
fraillt
2017-09-25 13:02:12 +03:00
parent f0508025f6
commit ab5cc8a2c0
29 changed files with 960 additions and 383 deletions

View File

@@ -1,5 +1,18 @@
# [3.0.0](https://github.com/fraillt/bitsery/compare/v2.0.1...v3.0.0) (2017-09-21)
new flexible syntax
traits changed,
container get isContiguous
text is separate from container, only has length, and addNUL
buffer traits removed difference type
improved reading, writing performance (because of isContiguous and difference_type)
todo write tests:
bufferreader accepts const data
# [3.0.0](https://github.com/fraillt/bitsery/compare/v2.0.1...v3.0.0) (2017-09-21)
### Features
* refactored interface, now works with C++11 compiler.

View File

@@ -1,6 +1,7 @@
#include <bitsery/bitsery.h>
#include <bitsery/ext/growable.h>
#include <array>
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
namespace MyTypes {
@@ -62,10 +63,11 @@ namespace MyTypes {
using namespace bitsery;
using Buffer =std::array<uint8_t, 1000000>;
//change configuration
struct NonDefaultConfig: public bitsery::DefaultConfig {
//change underlying buffer
using BufferType = std::array<uint8_t, 1000000>;
using BufferType = Buffer;
};
@@ -76,7 +78,7 @@ int main() {
//create serializer
//1) create buffer to store data
std::array<uint8_t, 1000000> buffer{};
Buffer buffer{};
//2) create buffer writer that is able to write bytes or bits to buffer
BasicBufferWriter<NonDefaultConfig> bw{buffer};
//3) create serializer

View File

@@ -35,14 +35,13 @@ namespace bitsery {
struct BasicBufferReader {
using BufferType = typename Config::BufferType;
using ValueType = typename details::BufferContainerTraits<BufferType>::TValue;
using ValueType = typename details::ContainerTraits<BufferType>::TValue;
using BufferIteratorType = typename details::BufferContainerTraits<BufferType>::TIterator;
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
BasicBufferReader(ValueType* begin, ValueType* end)
:_pos{begin},
_end{end},
_session{*this, _pos, _end}
BasicBufferReader(BufferIteratorType data, BufferIteratorType end)
: _bufferContext{data, end},
_session{*this, _bufferContext}
{
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
@@ -52,7 +51,7 @@ namespace bitsery {
}
BasicBufferReader(BufferRange<BufferIteratorType> range)
:BasicBufferReader(std::addressof(*range.begin()), std::addressof(*range.end())) {
:BasicBufferReader(range.begin(), range.end()) {
static_assert(std::is_same<
typename std::iterator_traits<BufferIteratorType>::iterator_category,
std::random_access_iterator_tag>::value,
@@ -114,24 +113,18 @@ namespace bitsery {
}
bool isCompletedSuccessfully() const {
return _pos == _end && !_session.hasActiveSessions();
return _bufferContext.isCompletedSuccessfully() && !_session.hasActiveSessions();
}
BufferReaderError getError() const {
auto res = std::distance(_end, _pos);
if (res > 0) {
auto err = static_cast<BufferReaderError>(res);
if (_session.hasActiveSessions() && err == BufferReaderError::BUFFER_OVERFLOW)
return BufferReaderError::NO_ERROR;
return err;
}
return BufferReaderError::NO_ERROR;
auto err = _bufferContext.getError();
if (_session.hasActiveSessions() && err == BufferReaderError::BUFFER_OVERFLOW)
return BufferReaderError::NO_ERROR;
return err;
}
void setError(BufferReaderError error) {
_end = _pos;
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
std::advance(_pos, static_cast<size_t>(error));
return _bufferContext.setError(error);
}
void beginSession() {
@@ -149,35 +142,21 @@ namespace bitsery {
}
private:
ValueType* _pos;
ValueType* _end;
details::ReadBufferContext<BufferType> _bufferContext;
ScratchType m_scratch{};
size_t m_scratchBits{};
typename std::conditional<Config::BufferSessionsEnabled,
details::BufferSessionsReader<BasicBufferReader<Config>, ValueType*>,
details::BufferSessionsReader<BasicBufferReader<Config>, details::ReadBufferContext<BufferType>>,
details::DisabledBufferSessionsReader<Config>>::type
_session;
template<typename T>
void directRead(T *v, size_t count) {
static_assert(!std::is_const<T>::value, "");
const auto bytesCount = sizeof(T) * count;
if (std::distance(_pos, _end) >= static_cast<typename details::BufferContainerTraits<BufferType>::TDifference>(bytesCount)) {
std::memcpy(reinterpret_cast<ValueType *>(v), _pos, bytesCount);
_pos += bytesCount;
//swap each byte if nessesarry
_swapDataBits(v, count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
} else {
//set everything to zeros
std::memset(v, 0, bytesCount);
if (getError() == BufferReaderError::NO_ERROR)
setError(BufferReaderError::BUFFER_OVERFLOW);
}
_bufferContext.read(reinterpret_cast<ValueType *>(v), sizeof(T) * count);
//swap each byte if nessesarry
_swapDataBits(v, count, std::integral_constant<bool,
Config::NetworkEndianness != details::getSystemEndianness()>{});
}
template<typename T>

View File

@@ -95,9 +95,9 @@ namespace bitsery {
template<typename Config>
struct BasicBufferWriter {
using BufferType = typename Config::BufferType;
using ValueType = typename details::BufferContainerTraits<BufferType>::TValue;
using ValueType = typename details::ContainerTraits<BufferType>::TValue;
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
using BufferContext = details::WriteBufferContext<BufferType, details::BufferContainerTraits<BufferType>::isResizable>;
using BufferContext = details::WriteBufferContext<BufferType, details::ContainerTraits<BufferType>::isResizable>;
explicit BasicBufferWriter(BufferType &buffer)
: _bufferContext{buffer}

View File

@@ -25,6 +25,7 @@
#define BITSERY_COMMON_H
#include <vector>
#include "traits/vector.h"
namespace bitsery {

View File

@@ -50,7 +50,8 @@ namespace bitsery {
template<typename T>
void object(T &&obj) {
details::SerializeFunction<BasicDeserializer, T>::invoke(*this, std::forward<T>(obj));
using TValue = typename std::decay<T>::type;
details::SerializeFunction<BasicDeserializer, TValue>::invoke(*this, std::forward<T>(obj));
}
template<typename T, typename Fnc>
@@ -58,25 +59,25 @@ namespace bitsery {
fnc(std::forward<T>(obj));
};
/*
* functionality, that enables simpler serialization syntax, by including additional header
*/
template<typename T, typename ... TArgs>
void archive(T &&head, TArgs &&... tail) {
//serialize object
details::ArchiveFunction<BasicDeserializer, T>::invoke(*this, std::forward<T>(head));
//expand other elements
archive(std::forward<TArgs>(tail)...);
}
/*
* value overloads
*/
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
void value(T &v) {
static_assert(std::numeric_limits<T>::is_iec559, "");
_reader.template readBytes<VSIZE>(reinterpret_cast<details::SAME_SIZE_UNSIGNED<T> &>(v));
}
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
void value(T &v) {
using UT = typename std::underlying_type<T>::type;
_reader.template readBytes<VSIZE>(reinterpret_cast<UT &>(v));
}
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void value(T &v) {
_reader.template readBytes<VSIZE>(v);
using TValue = typename details::IntegralFromFundamental<T>::TValue;
_reader.template readBytes<VSIZE>(reinterpret_cast<TValue &>(v));
}
/*
@@ -84,29 +85,26 @@ namespace bitsery {
*/
template<typename T, typename Ext, typename Fnc>
void ext(T &obj, Ext &&extension, Fnc &&fnc) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportLambdaOverload,
void ext(T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::ExtensionTraits<Ext,T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.deserialize(*this, _reader, obj, std::forward<Fnc>(fnc));
};
template<size_t VSIZE, typename T, typename Ext>
void ext(T &obj, Ext &&extension) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportValueOverload,
void ext(T &obj, const Ext &extension) {
static_assert(details::ExtensionTraits<Ext,T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, _reader, obj, [this](VType &v) { value<VSIZE>(v); });
};
template<typename T, typename Ext>
void ext(T &obj, Ext &&extension) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportObjectOverload,
void ext(T &obj, const Ext &extension) {
static_assert(details::ExtensionTraits<Ext,T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.deserialize(*this, _reader, obj, [this](VType &v) { object(v); });
};
@@ -135,42 +133,21 @@ namespace bitsery {
template<size_t VSIZE, typename T>
void text(T &str, size_t maxSize) {
static_assert(details::TextTraits<T>::isResizable,
static_assert(details::ContainerTraits<T>::isResizable,
"use text(T&) overload without `maxSize` for static containers");
size_t size;
details::readSize(_reader, size, maxSize);
details::TextTraits<T>::resize(str, size);
auto begin = std::begin(str);
auto end = std::next(begin, size);
procContainer<VSIZE>(begin, end, std::true_type{});
//null terminated character at the end
*end = {};
size_t length;
details::readSize(_reader, length, maxSize);
details::ContainerTraits<T>::resize(str, length + (details::TextTraits<T>::addNUL ? 1u : 0u));
procText<VSIZE>(str, length);
}
template<size_t VSIZE, typename T>
void text(T &str) {
static_assert(!details::TextTraits<T>::isResizable,
static_assert(!details::ContainerTraits<T>::isResizable,
"use text(T&, size_t) overload with `maxSize` for dynamic containers");
size_t size;
auto begin = std::begin(str);
auto containerEnd = std::end(str);
assert(begin != containerEnd);
details::readSize(_reader, size, static_cast<size_t>(std::distance(begin, containerEnd) - 1));
//end of string, not en
auto end = std::next(begin, size);
procContainer<VSIZE>(std::begin(str), std::end(str), std::true_type{});
//null terminated character at the end
*end = {};
}
template<size_t VSIZE, typename T, size_t N>
void text(T (&str)[N]) {
size_t size;
details::readSize(_reader, size, N - 1);
auto first = std::begin(str);
procContainer<VSIZE>(first, std::next(first, size), std::true_type{});
//null-terminated string
str[size] = {};
size_t length;
details::readSize(_reader, length, details::ContainerTraits<T>::size(str));
procText<VSIZE>(str, length);
}
/*
@@ -197,7 +174,7 @@ namespace bitsery {
size_t size{};
details::readSize(_reader, size, maxSize);
details::ContainerTraits<T>::resize(obj, size);
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
@@ -223,7 +200,7 @@ namespace bitsery {
static_assert(!details::ContainerTraits<T>::isResizable,
"use container(T&, size_t) overload with `maxSize` for dynamic containers");
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::false_type{});
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
@@ -233,23 +210,6 @@ namespace bitsery {
procContainer(std::begin(obj), std::end(obj));
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
void container(T (&arr)[N], Fnc &&fnc) {
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, size_t N>
void container(T (&arr)[N]) {
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
}
template<typename T, size_t N>
void container(T (&arr)[N]) {
procContainer(std::begin(arr), std::end(arr));
}
void align() {
_reader.align();
}
@@ -289,14 +249,14 @@ namespace bitsery {
template<typename T>
void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T, size_t N>
void text1b(T (&str)[N]) { text<1>(str); }
template<typename T>
void text1b(T &str) { text<1>(str); }
template<typename T, size_t N>
void text2b(T (&str)[N]) { text<2>(str); }
template<typename T>
void text2b(T &str) { text<2>(str); }
template<typename T, size_t N>
void text4b(T (&str)[N]) { text<4>(str); }
template<typename T>
void text4b(T &str) { text<4>(str); }
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
@@ -322,19 +282,6 @@ namespace bitsery {
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
template<typename T, size_t N>
void container1b(T (&arr)[N]) { container<1>(arr); }
template<typename T, size_t N>
void container2b(T (&arr)[N]) { container<2>(arr); }
template<typename T, size_t N>
void container4b(T (&arr)[N]) { container<4>(arr); }
template<typename T, size_t N>
void container8b(T (&arr)[N]) { container<8>(arr); }
private:
BasicBufferReader<Config> &_reader;
void* _context;
@@ -351,8 +298,10 @@ 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) {
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last)
_reader.template readBuffer<VSIZE>(&(*first), std::distance(first, last));
_reader.template readBuffer<VSIZE>(reinterpret_cast<TIntegral*>(&(*first)), std::distance(first, last));
};
//process by calling functions
@@ -369,6 +318,17 @@ namespace bitsery {
object(*first);
};
template <size_t VSIZE, typename T>
void procText(T& str, size_t length) {
auto begin = std::begin(str);
//end of string, not end of container
auto end = std::next(begin, length);
procContainer<VSIZE>(begin, end, std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
//null terminated character at the end
if (details::TextTraits<T>::addNUL)
*end = {};
}
//these are dummy functions for extensions that have TValue = void
void object(details::DummyType&) {
@@ -379,6 +339,10 @@ namespace bitsery {
}
//dummy function, that stops archive variadic arguments expansion
void archive() {
}
};
//helper type

View File

@@ -140,8 +140,8 @@ namespace bitsery {
template <typename Config>
struct DisabledBufferSessionsReader {
template <typename TReader, typename TIterator>
DisabledBufferSessionsReader(TReader& , TIterator& , TIterator& ) {
template <typename TReader, typename TBufferContext>
DisabledBufferSessionsReader(TReader& , TBufferContext& ) {
}
void begin() {
@@ -208,38 +208,42 @@ namespace bitsery {
std::stack<size_t> _sessionIndex;
};
template <typename TReader, typename TIterator>
template <typename TReader, typename TBufferContext>
struct BufferSessionsReader {
BufferSessionsReader(TReader& r, TIterator& begin, TIterator& end)
using TIterator = typename TReader::BufferIteratorType;
BufferSessionsReader(TReader& r, TBufferContext& bufferContext)
:_reader{r},
_begin{begin},
_pos{begin},
_end{end}
_begin{bufferContext._pos},
_context{bufferContext}
{
}
void begin() {
if (_sessions.empty())
initializeSessions();
if (_sessions.empty()) {
if (!initializeSessions())
return;
}
//save end position for current session
_sessionsStack.push(_end);
_sessionsStack.push(_context._end);
if (_nextSessionIt != std::end(_sessions)) {
if (std::distance(_pos, _end) > 0) {
if (std::distance(_context._pos, _context._end) > 0) {
//set end position for new session
auto newEnd = std::next(_begin, *_nextSessionIt);
if (std::distance(newEnd, _end) < 0)
if (std::distance(newEnd, _context._end) < 0)
{
//new session cannot end further than current end
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
return;
}
_end = newEnd;
_context._end = newEnd;
++_nextSessionIt;
}
//if we reached the end, means that there is no more data to read, hence there is no more sessions to advance to
} else {
//there is no data to read anymore
//pos == end or buffer overflow while session is active
if (!(_pos == _end || _reader.getError() == BufferReaderError::NO_ERROR)) {
if (!(_context._pos == _context._end || _reader.getError() == BufferReaderError::NO_ERROR)) {
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
}
}
@@ -252,18 +256,18 @@ namespace bitsery {
//_pos == _end : same versions
//distance(_pos,_end) > 0: reading newer version
//getError() == BUFFER_OVERFLOW: reading older version
auto dist = std::distance(_pos, _end);
auto dist = std::distance(_context._pos, _context._end);
if (dist > 0) {
//newer version might have some inner sessions, try to find the one after current ends
auto currPos = static_cast<size_t>(std::distance(_begin, _end));
auto currPos = static_cast<size_t>(std::distance(_begin, _context._end));
for (; _nextSessionIt != std::end(_sessions); ++_nextSessionIt) {
if (*_nextSessionIt > currPos)
break;
}
}
_pos = _end;
_context._pos = _context._end;
//restore end position
_end = _sessionsStack.top();
_context._end = _sessionsStack.top();
_sessionsStack.pop();
}
}
@@ -275,23 +279,21 @@ namespace bitsery {
private:
TReader& _reader;
TIterator _begin;
TIterator& _pos;
TIterator& _end;
TBufferContext& _context;
std::vector<size_t> _sessions{};
std::vector<size_t>::iterator _nextSessionIt{};
std::stack<TIterator> _sessionsStack{};
void initializeSessions() {
bool initializeSessions() {
//save current position
auto currPos = _pos;
auto currPos = _context._pos;
//read size
if (std::distance(_pos, _end) < 2) {
if (std::distance(_context._pos, _context._end) < 2) {
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
return;
return false;
}
auto endSessionsSizesIt = std::next(_end, -2);
_pos = endSessionsSizesIt;
auto endSessionsSizesIt = std::next(_context._end, -2);
_context._pos = endSessionsSizesIt;
size_t sessionsOffset{};
uint16_t high;
_reader.template readBytes<2>(high);
@@ -299,10 +301,10 @@ namespace bitsery {
if (high >= 0x8000u) {
endSessionsSizesIt = std::next(endSessionsSizesIt, -2);
_pos = endSessionsSizesIt;
if (std::distance(_begin, _pos) < 0) {
_context._pos = endSessionsSizesIt;
if (std::distance(_begin, _context._pos) < 0) {
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
return;
return false;
}
uint16_t low;
_reader.template readBytes<2>(low);
@@ -313,25 +315,26 @@ namespace bitsery {
} else
sessionsOffset = high;
auto bufferSize = std::distance(_begin, _end);
auto bufferSize = std::distance(_begin, _context._end);
if (static_cast<size_t>(bufferSize) < sessionsOffset) {
_reader.setError(BufferReaderError::INVALID_BUFFER_DATA);
return;
return false;
}
//we can initialy resizes to this value, and we'll shrink it after reading
//read session sizes
auto sessionsIt = std::back_inserter(_sessions);
_pos = std::next(_end, -sessionsOffset);
while (std::distance(_pos, endSessionsSizesIt) > 0) {
_context._pos = std::next(_context._end, -sessionsOffset);
while (std::distance(_context._pos, endSessionsSizesIt) > 0) {
size_t size;
details::readSize(_reader, size, bufferSize);
*sessionsIt++ = size;
}
_sessions.shrink_to_fit();
//set iterators to data
_pos = currPos;
_end = std::next(_end, -sessionsOffset);
_context._pos = currPos;
_context._end = std::next(_context._end, -sessionsOffset);
_nextSessionIt = std::begin(_sessions);//set before first session;
return true;
}
};
@@ -344,77 +347,133 @@ namespace bitsery {
template<typename Buffer>
class WriteBufferContext<Buffer, false> {
public:
using TValue = typename BufferContainerTraits<Buffer>::TValue;
using TValue = typename ContainerTraits<Buffer>::TValue;
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
using TDifference = typename BufferContainerTraits<Buffer>::TDifference;
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer},
_outIt{std::addressof(*std::begin(buffer))},
_end{std::addressof(*std::end(buffer))}
_outIt{std::begin(buffer)},
_end{std::end(buffer)}
{
}
void write(const TValue *data, size_t size) {
assert(std::distance(_outIt, _end) >= static_cast<TDifference>(size));
memcpy(_outIt, data, size);
auto tmp = _outIt;
_outIt += size;
assert(std::distance(_outIt, _end) >= 0);
memcpy(std::addressof(*tmp), data, size);
}
BufferRange<TIterator> getWrittenRange() const {
auto begin = std::begin(_buffer);
return BufferRange<TIterator>{begin, std::next(begin, _outIt - std::addressof(*begin))};
return BufferRange<TIterator>{std::begin(_buffer), _outIt};
}
private:
Buffer &_buffer;
TValue* _outIt;
TValue* _end;
TIterator _outIt;
TIterator _end;
};
template<typename Buffer>
class WriteBufferContext<Buffer, true> {
public:
using TValue = typename BufferContainerTraits<Buffer>::TValue;
using TValue = typename ContainerTraits<Buffer>::TValue;
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
using TDifference = typename BufferContainerTraits<Buffer>::TDifference;
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer}
{
//resize buffer immediately, because we need output iterator at valid position
if (ContainerTraits<Buffer>::size(buffer) == 0u) {
BufferContainerTraits<Buffer>::increaseBufferSize(_buffer);
}
getIterators(0);
}
void write(const TValue *data, size_t size) {
if ((_end - _outIt) >= static_cast< TDifference >(size)) {
std::memcpy(_outIt, data, size);
_outIt += size;
auto tmp = _outIt;
_outIt += size;
if (std::distance(_outIt , _end) >= 0) {
std::memcpy(std::addressof(*tmp), data, size);
} else {
_outIt -= size;
//get current position before invalidating iterators
auto pos = std::distance(std::addressof(*std::begin(_buffer)), _outIt);
const auto pos = std::distance(std::begin(_buffer), _outIt);
//increase container size
BufferContainerTraits<Buffer>::increaseBufferSize(_buffer);
//restore iterators
getIterators(pos);
getIterators(static_cast<size_t>(pos));
write(data, size);
}
}
BufferRange<TIterator> getWrittenRange() const {
auto begin = std::begin(_buffer);
return BufferRange<TIterator>{begin, std::next(begin, _outIt - std::addressof(*begin))};
return BufferRange<TIterator>{std::begin(_buffer), _outIt};
}
private:
void getIterators(TDifference writePos) {
_end = std::addressof(*std::end(_buffer));
_outIt = std::addressof(*std::next(std::begin(_buffer), writePos));
void getIterators(size_t writePos) {
_end = std::end(_buffer);
_outIt = std::next(std::begin(_buffer), writePos);
}
Buffer &_buffer;
TValue* _outIt;
TValue* _end;
TIterator _outIt;
TIterator _end;
};
template <typename Buffer>
class ReadBufferContext {
public:
using TValue = typename ContainerTraits<Buffer>::TValue;
using TIterator = typename BufferContainerTraits<Buffer>::TIterator;
ReadBufferContext(TIterator begin, TIterator end)
:_pos{begin},
_end{end}
{
}
void read(TValue* data, size_t size) {
//for optimization
auto tmp = _pos;
_pos += size;
if (std::distance(_pos, _end) >= 0) {
std::memcpy(data, std::addressof(*tmp), size);
} else {
_pos -= size;
//set everything to zeros
std::memset(data, 0, size);
if (getError() == BufferReaderError::NO_ERROR)
setError(BufferReaderError::BUFFER_OVERFLOW);
}
}
BufferReaderError getError() const {
auto res = std::distance(_end, _pos);
if (res > 0) {
auto err = static_cast<BufferReaderError>(res);
return err;
}
return BufferReaderError::NO_ERROR;
}
void setError(BufferReaderError error) {
_end = _pos;
//to avoid creating temporary for error state, mark an error by passing _pos after the _end
std::advance(_pos, static_cast<size_t>(error));
}
bool isCompletedSuccessfully() const {
return _pos == _end;
}
TIterator _pos;
TIterator _end;
};
}

View File

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

View File

@@ -28,25 +28,54 @@
namespace bitsery {
//this allows to call private serialize method for the class
//just make friend it to that class
struct Access {
template<typename S, typename T>
static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) {
obj.serialize(s);
}
};
namespace details {
template<typename T, typename Enable = void>
struct SAME_SIZE_UNSIGNED_TYPE {
using type = typename std::make_unsigned<T>::type;
//used for extensions, when extension TValue = void
struct DummyType {
};
/*
* this includes all integral types floats and enums(except bool)
*/
template<typename T>
struct IsFundamentalType : std::integral_constant<bool,
std::is_enum<T>::value
|| std::is_floating_point<T>::value
|| std::is_integral<T>::value> {
};
template<typename T, typename Integral = void>
struct IntegralFromFundamental {
using TValue = T;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_enum<T>::value>::type> {
using type = typename std::make_unsigned<typename std::underlying_type<T>::type>::type;
struct IntegralFromFundamental<T, typename std::enable_if<std::is_enum<T>::value>::type> {
using TValue = typename std::underlying_type<T>::type;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
using type = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
struct IntegralFromFundamental<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
using TValue = typename std::conditional<std::is_same<T, float>::value, uint32_t, uint64_t>::type;
};
template<typename T>
using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE<T>::type;
struct UnsignedFromFundamental {
using type = typename std::make_unsigned<typename IntegralFromFundamental<T>::TValue>::type;
};
template<typename T>
using SAME_SIZE_UNSIGNED = typename UnsignedFromFundamental<T>::type;
/*
* functions for object serialization
@@ -54,28 +83,62 @@ namespace bitsery {
template<typename S, typename T, typename Enabled = void>
struct SerializeFunction {
static void invoke(S &s, T &v) {
static_assert(!std::is_void<Enabled>::value,
"\nPlease define 'serialize' function for your type:\n"
"\nPlease define 'serialize' function for your type (inside or outside of class):\n"
" template<typename S>\n"
" void serialize(S& s, <YourType>& o)\n"
" void serialize(S& s)\n"
" {\n"
" ...\n"
" }\n");
}
};
//check for serialize(s,o) support
template<typename S, typename T>
struct SerializeFunction<S, T, typename std::enable_if<
std::is_same<void, decltype(serialize(std::declval<S &>(), std::declval<T &>()))>::value
std::is_same<void, decltype((void) serialize(std::declval<S &>(), std::declval<T &>()))>::value
>::type> {
static void invoke(S &s, T &v) {
serialize(s, v);
}
};
//used for extensions, when extension TValue = void
struct DummyType {
//check for o.serialize(s) support through static class Access
template<typename S, typename T>
struct SerializeFunction<S, T, typename std::enable_if<
std::is_same<void, decltype(Access::serialize(std::declval<S &>(), std::declval<T &>()))>::value
>::type> {
static void invoke(S &s, T &v) {
Access::serialize(s, v);
}
};
/*
* functions for object serialization
*/
template<typename S, typename T, typename Enabled = void>
struct ArchiveFunction {
static void invoke(S &s, T &v) {
static_assert(!std::is_void<Enabled>::value,
"\nPlease include 'flexible.h' to use 'archive' function:\n");
}
};
template<typename S, typename T>
struct ArchiveFunction<S, T, typename std::enable_if<
std::is_same<void, decltype((void)archiveProcess(std::declval<S &>(), std::declval<T &&>()))>::value
>::type> {
static void invoke(S &s, T &&obj) {
archiveProcess(s, std::forward<T>(obj));
}
};
/*

View File

@@ -24,20 +24,10 @@
#define BITSERY_DETAILS_TRAITS_H
#include <type_traits>
#include <string>
namespace bitsery {
namespace details {
/*
* helper traits that is used internaly, or by other traits
*/
template <typename T, typename = int>
struct IsResizable : std::false_type {};
template <typename T>
struct IsResizable <T, decltype((void)std::declval<T>().resize(1u), 0)> : std::true_type {};
/*
* core library traits, used to extend library for custom types
*/
@@ -63,100 +53,88 @@ namespace bitsery {
template<typename T>
struct ContainerTraits {
using TValue = typename T::value_type;
//default behaviour is resizable if container has method T::resize(size_t)
static constexpr bool isResizable = IsResizable<T>::value;
using TValue = void;
static constexpr bool isResizable = false;
//contiguous arrays has oppurtunity to memcpy whole buffer directly when using funtamental types
//contiguous doesn't nesessary equal to random access iterator.
//contiguous hopefully will be available in c++20
static constexpr bool isContiguous = false;
//resize function, called only if container is resizable
static void resize(T& container, size_t size) {
container.resize(size);
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
}
//get container size
static size_t size(const T& container) {
return container.size();
static_assert(std::is_void<T>::value,
"Define ContainerTraits or include from <bitsery/traits/...> to use as container");
return 0u;
}
};
//specialization for C style array
template<typename T, size_t N>
struct ContainerTraits<T[N]> {
using TValue = T;
static constexpr bool isResizable = IsResizable<T>::value;
static void resize(T (&container)[N], size_t size) {
}
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const T (&container)[N]) {
return N;
}
};
//traits for text
//specialization for initializer list, even though it cannot be deserialized to.
template<typename T>
struct TextTraits {
static constexpr bool isResizable = true;
//resize is without null-terminated character as with std::string,
//but null terminated character will always be written
//if you container doesn't add null-terminated character automaticaly, resize it to size+1;
static void resize(T& container, size_t size) {
container.resize(size);
}
//used for serialization to get text length
//length is until null-terminated character, size and length might not be equal
static size_t length(const T& container) {
auto begin = std::begin(container);
using TValue = typename std::decay<decltype(*begin)>::type;
return std::char_traits<TValue>::length(std::addressof(*begin));
}
};
//text traits specialization for std::string
//for std::string return length as size(), for faster performance, so we don't need to traverse string to find null-terminated characeter
//although it is not correct behaviour, meaning that string might have null-terminated characters in the middle,
//but in this case it your decision if you store buffer in string and serialize it as a text.
template<typename ... Args>
struct TextTraits<std::basic_string<Args...>> {
static constexpr bool isResizable = true;
//resize is without null-terminated character as with std::string,
//but null terminated character will always be written
//if you container doesn't add null-terminated character automaticaly, resize it to size+1;
static void resize(std::basic_string<Args...>& container, size_t size) {
container.resize(size);
}
//used for serialization to get text length
//length is until null-terminated character, size and length might not be equal
static size_t length(const std::basic_string<Args...>& container) {
struct ContainerTraits<std::initializer_list<T>> {
using TValue = T;
static constexpr bool isResizable = false;
static constexpr bool isContiguous = true;
static size_t size(const std::initializer_list<T>& container) {
return container.size();
}
};
//traits for text, default adds null-terminated character at the end
template<typename T>
struct TextTraits {
//if container is not null-terminated by default, add NUL at the end
static constexpr bool addNUL = true;
//get length of null terminated container
static size_t length(const T& container) {
static_assert(std::is_void<T>::value,
"Define TextTraits or include from <bitsery/traits/...> to use as text");
return 0u;
}
};
//traits only for buffer reader/writer
template <typename T>
struct BufferContainerTraits: public ContainerTraits<T> {
struct BufferContainerTraits {
//this function is only applies to resizable containers
//this function is only used by BufferWriter, when writing data to buffer,
//it is called only current buffer size is not enough to write.
//it is used to dramaticaly improve performance by updating buffer directly
//instead of using back_insert_iterator to append each byte to buffer.
//thats why BufferWriter return range iterators
static void increaseBufferSize(T& container) {
//use default implementation behaviour;
//call push_back to use default resize strategy
container.push_back({});
//after allocation resize to take all capacity
container.resize(container.capacity());
static_assert(std::is_void<T>::value,
"Define BufferContainerTraits or include from <bitsery/traits/...> to use as buffer");
}
using TDifference = typename T::difference_type;
using TIterator = typename T::iterator;
using TIterator = void;
};
//specialization for c-style buffer
template <typename T, size_t N>
struct BufferContainerTraits<T[N]> {
using TIterator = T*;
};
}

100
include/bitsery/flexible.h Normal file
View File

@@ -0,0 +1,100 @@
//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_FLEXIBLE_H
#define BITSERY_FLEXIBLE_H
#include "details/serialization_common.h"
#include "bitsery/details/flexible_common.h"
namespace bitsery {
namespace flexible {
//overload when T is reference type
template<typename S, typename T>
void archiveProcessImpl(S &s, T &&head, std::true_type) {
s.object(std::forward<T>(head));
};
//overload when T is rvalue type, only allowable for behaviour modifying functions for deserializer
template<typename S, typename T>
void archiveProcessImpl(S &s, T &&head, std::false_type) {
static_assert(std::is_base_of<ArchiveWrapperFnc, T>::value,
"\nOnly archive behaviour modifying functions can be passed by rvalue to deserializer\n");
serialize(s, head);
};
}
//define function that enables s.archive(....) usage
template<typename S, typename T>
void archiveProcess(S &s, T &&head) {
flexible::archiveProcessImpl(s, std::forward<T>(head), std::is_reference<T>{});
}
//wrapper functions that enables to serialize as container or string
template<typename T, size_t N>
flexible::CArray<T, N, true> asText(T (&str)[N]) {
return {str};
};
template<typename T, size_t N>
flexible::CArray<T, N, false> asContainer(T (&obj)[N]) {
return {obj};
};
template <typename T>
flexible::MaxSize<T> maxSize(T& obj, size_t max) {
return {obj, max};
}
//define serialize function for fundamental types
template<typename S>
void serialize(S &s, bool &v) {
s.boolByte(v);
}
template<typename S, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
void serialize(S &s, T &v) {
s.template value<sizeof(T)>(v);
};
//define serialization for c-style container
//if array is integral type, specify explicitly how to process: as text or container
template<typename S, typename T, size_t N, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void serialize(S &s, T (&v)[N]) {
static_assert(N == 0,
"\nPlease use 'asText(obj)' or 'asContainer(obj)' when using c-style array with integral types\n");
};
template<typename S, typename T, size_t N, typename std::enable_if<!std::is_integral<T>::value>::type * = nullptr>
void serialize(S &s, T (&obj)[N]) {
s.container(obj);
};
}
#endif //BITSERY_FLEXIBLE_H

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,9 +30,6 @@
namespace bitsery {
template<typename Config>
class BasicSerializer {
public:
@@ -59,24 +56,25 @@ namespace bitsery {
fnc(const_cast<T& >(obj));
};
/*
* functionality, that enables simpler serialization syntax, by including additional header
*/
template<typename T, typename ... TArgs>
void archive(T &&head, TArgs &&... tail) {
//serialize object
details::ArchiveFunction<BasicSerializer, T>::invoke(*this, std::forward<T>(head));
//expand other elements
archive(std::forward<TArgs>(tail)...);
}
/*
* value overloads
*/
template<size_t VSIZE, typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr>
template<size_t VSIZE, typename T, typename std::enable_if<details::IsFundamentalType<T>::value>::type * = nullptr>
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));
}
template<size_t VSIZE, typename T, typename std::enable_if<std::is_enum<T>::value>::type * = nullptr>
void value(const T &v) {
_writter.template writeBytes<VSIZE>(reinterpret_cast<const typename std::underlying_type<T>::type &>(v));
}
template<size_t VSIZE, typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
void value(const T &v) {
_writter.template writeBytes<VSIZE>(v);
using TValue = typename details::IntegralFromFundamental<T>::TValue;
_writter.template writeBytes<VSIZE>(reinterpret_cast<const TValue &>(v));
}
/*
@@ -84,29 +82,26 @@ namespace bitsery {
*/
template<typename T, typename Ext, typename Fnc>
void ext(const T &obj, Ext &&extension, Fnc &&fnc) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportLambdaOverload,
void ext(const T &obj, const Ext &extension, Fnc &&fnc) {
static_assert(details::ExtensionTraits<Ext,T>::SupportLambdaOverload,
"extension doesn't support overload with lambda");
extension.serialize(*this, _writter, obj, std::forward<Fnc>(fnc));
};
template<size_t VSIZE, typename T, typename Ext>
void ext(const T &obj, Ext &&extension) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportValueOverload,
void ext(const T &obj, const Ext &extension) {
static_assert(details::ExtensionTraits<Ext,T>::SupportValueOverload,
"extension doesn't support overload with `value<N>`");
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, _writter, obj, [this](VType &v) { value<VSIZE>(v); });
};
template<typename T, typename Ext>
void ext(const T &obj, Ext &&extension) {
using ExtType = typename std::decay<Ext>::type;
static_assert(details::ExtensionTraits<ExtType,T>::SupportObjectOverload,
void ext(const T &obj, const Ext &extension) {
static_assert(details::ExtensionTraits<Ext,T>::SupportObjectOverload,
"extension doesn't support overload with `object`");
using ExtVType = typename details::ExtensionTraits<ExtType, T>::TValue;
using ExtVType = typename details::ExtensionTraits<Ext, T>::TValue;
using VType = typename std::conditional<std::is_void<ExtVType>::value, details::DummyType, ExtVType>::type;
extension.serialize(*this, _writter, obj, [this](VType &v) { object(v); });
};
@@ -129,37 +124,16 @@ namespace bitsery {
template<size_t VSIZE, typename T>
void text(const T &str, size_t maxSize) {
static_assert(details::TextTraits<T>::isResizable,
static_assert(details::ContainerTraits<T>::isResizable,
"use text(const T&) overload without `maxSize` for static container");
auto size = details::TextTraits<T>::length(str);
//size can be equal to maxSize
assert(size <= maxSize);
details::writeSize(_writter, size);
auto begin = std::begin(str);
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
procText<VSIZE>(str, maxSize);
}
template<size_t VSIZE, typename T>
void text(const T &str) {
static_assert(!details::TextTraits<T>::isResizable,
static_assert(!details::ContainerTraits<T>::isResizable,
"use text(const T&, size_t) overload with `maxSize` for dynamic containers");
auto size = details::TextTraits<T>::length(str);
auto begin = std::begin(str);
auto end = std::end(str);
//size must be less than container capacity, because we need to store null-terminated character
assert(size < std::distance(begin, end));
details::writeSize(_writter, size);
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
}
template<size_t VSIZE, typename T, size_t N>
void text(const T (&str)[N]) {
auto size = details::TextTraits<T[N]>::length(str);
assert(size < N);
details::writeSize(_writter, size);
auto begin = std::begin(str);
procContainer<VSIZE>(begin, std::next(begin, size), std::true_type{});
procText<VSIZE>(str, details::ContainerTraits<T>::size(str));
}
/*
@@ -187,8 +161,8 @@ namespace bitsery {
auto size = details::ContainerTraits<T>::size(obj);
assert(size <= maxSize);
details::writeSize(_writter, 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{});
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
@@ -215,8 +189,7 @@ namespace bitsery {
static_assert(!details::ContainerTraits<T>::isResizable,
"use container(const T&, size_t) overload with `maxSize` for dynamic containers");
static_assert(VSIZE > 0, "");
//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{});
procContainer<VSIZE>(std::begin(obj), std::end(obj), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
}
template<typename T>
@@ -226,24 +199,6 @@ namespace bitsery {
procContainer(std::begin(obj), std::end(obj));
}
//c-style array overloads
template<typename T, size_t N, typename Fnc>
void container(const T (&arr)[N], Fnc &&fnc) {
procContainer(std::begin(arr), std::end(arr), std::forward<Fnc>(fnc));
}
template<size_t VSIZE, typename T, size_t N>
void container(const T (&arr)[N]) {
static_assert(VSIZE > 0, "");
procContainer<VSIZE>(std::begin(arr), std::end(arr), std::true_type{});
}
template<typename T, size_t N>
void container(const T (&arr)[N]) {
procContainer(std::begin(arr), std::end(arr));
}
void align() {
_writter.align();
}
@@ -283,14 +238,14 @@ namespace bitsery {
template<typename T>
void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); }
template<typename T, size_t N>
void text1b(const T (&str)[N]) { text<1, T, N>(str); }
template<typename T>
void text1b(const T &str) { text<1>(str); }
template<typename T, size_t N>
void text2b(const T (&str)[N]) { text<2, T, N>(str); }
template<typename T>
void text2b(const T &str) { text<2>(str); }
template<typename T, size_t N>
void text4b(const T (&str)[N]) { text<4, T, N>(str); }
template<typename T>
void text4b(const T &str) { text<4>(str); }
template<typename T>
void container1b(T &&obj, size_t maxSize) { container<1>(std::forward<T>(obj), maxSize); }
@@ -316,18 +271,6 @@ namespace bitsery {
template<typename T>
void container8b(T &&obj) { container<8>(std::forward<T>(obj)); }
template<typename T, size_t N>
void container1b(const T (&arr)[N]) { container<1>(arr); }
template<typename T, size_t N>
void container2b(const T (&arr)[N]) { container<2>(arr); }
template<typename T, size_t N>
void container4b(const T (&arr)[N]) { container<4>(arr); }
template<typename T, size_t N>
void container8b(const T (&arr)[N]) { container<8>(arr); }
private:
BasicBufferWriter<Config> &_writter;
void* _context;
@@ -344,7 +287,10 @@ 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) {
_writter.template writeBuffer<VSIZE>(&(*first), std::distance(first, last));
using TValue = typename std::decay<decltype(*first)>::type;
using TIntegral = typename details::IntegralFromFundamental<TValue>::TValue;
if (first != last)
_writter.template writeBuffer<VSIZE>(reinterpret_cast<const TIntegral*>(&(*first)), std::distance(first, last));
};
//process by calling functions
@@ -356,6 +302,16 @@ namespace bitsery {
}
};
//process text,
template<size_t VSIZE, typename T>
void procText(const T& str, size_t maxSize) {
auto length = details::TextTraits<T>::length(str);
assert((length + (details::TextTraits<T>::addNUL ? 1u : 0u)) <= maxSize);
details::writeSize(_writter, length);
auto begin = std::begin(str);
procContainer<VSIZE>(begin, std::next(begin, length), std::integral_constant<bool, details::ContainerTraits<T>::isContiguous>{});
};
//process object types
template<typename It>
void procContainer(It first, It last) {
@@ -373,6 +329,10 @@ namespace bitsery {
}
//dummy function, that stops archive variadic arguments expansion
void archive() {
}
};
//helper type

View File

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

View File

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

View File

@@ -145,7 +145,7 @@ struct IntegralUnsignedTypes {
TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) {
//fill initial values
static_assert(sizeof(bitsery::details::BufferContainerTraits<DefaultConfig::BufferType>::TValue) == 1, "currently only 1 byte size, value size is supported");
static_assert(sizeof(bitsery::details::ContainerTraits<DefaultConfig::BufferType>::TValue) == 1, "currently only 1 byte size, value size is supported");
//fill initial values
constexpr IntegralUnsignedTypes src {
0x0000334455667788,//bits 19

View File

@@ -22,6 +22,7 @@
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using testing::Eq;
using bitsery::BufferWriter;

View File

@@ -24,6 +24,8 @@
#include <bitsery/buffer_writer.h>
#include <bitsery/buffer_reader.h>
#include <bitsery/details/serialization_common.h>
#include <bitsery/traits/array.h>
#include <bitsery/traits/vector.h>
using testing::Eq;
using testing::ContainerEq;

View File

@@ -25,9 +25,12 @@
#include <gmock/gmock.h>
#include <algorithm>
#include <numeric>
#include <deque>
#include <list>
// #include <deque>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <bitsery/traits/list.h>
#include <bitsery/traits/deque.h>
using testing::ContainerEq;

View File

@@ -26,7 +26,7 @@
#include <bitsery/ext/container_map.h>
#include <bitsery/ext/entropy.h>
#include <unordered_map>
#include <map>
#include <bitsery/traits/string.h>
using ContainerMap = bitsery::ext::ContainerMap;

View File

@@ -24,8 +24,7 @@
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/ext/entropy.h>
#include <vector>
#include <list>
#include <bitsery/traits/list.h>
using namespace testing;

View File

@@ -29,7 +29,6 @@ using namespace testing;
using bitsery::ext::Growable;
using Buffer = typename bitsery::DefaultConfig::BufferType;
using DiffType = typename bitsery::details::BufferContainerTraits<Buffer>::TDifference;
struct DataV1 {
int32_t v1;

View File

@@ -23,6 +23,8 @@
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
#include <bitsery/traits/array.h>
using testing::Eq;
using testing::StrEq;

View File

@@ -23,6 +23,8 @@
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/string.h>
using namespace testing;
TEST(SerializeText, BasicString) {
@@ -113,6 +115,5 @@ TEST(SerializeText, WhenCArrayNotNullterminatedThenAssert) {
char16_t t1[CARR_LENGTH]{u"some text"};
//make last character not nullterminated
t1[CARR_LENGTH-1] = 'x';
EXPECT_DEATH(ctx.createSerializer().text<2>(t1), "");
}