diff --git a/CHANGELOG.md b/CHANGELOG.md index 93c955f..dc96588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/examples/flatbuffers_tutorial_data.cpp b/examples/flatbuffers_tutorial_data.cpp index 49d2be0..53560af 100644 --- a/examples/flatbuffers_tutorial_data.cpp +++ b/examples/flatbuffers_tutorial_data.cpp @@ -1,6 +1,7 @@ #include #include -#include +#include +#include namespace MyTypes { @@ -62,10 +63,11 @@ namespace MyTypes { using namespace bitsery; +using Buffer =std::array; //change configuration struct NonDefaultConfig: public bitsery::DefaultConfig { //change underlying buffer - using BufferType = std::array; + using BufferType = Buffer; }; @@ -76,7 +78,7 @@ int main() { //create serializer //1) create buffer to store data - std::array buffer{}; + Buffer buffer{}; //2) create buffer writer that is able to write bytes or bits to buffer BasicBufferWriter bw{buffer}; //3) create serializer diff --git a/include/bitsery/buffer_reader.h b/include/bitsery/buffer_reader.h index 7c93543..ff21f80 100644 --- a/include/bitsery/buffer_reader.h +++ b/include/bitsery/buffer_reader.h @@ -35,14 +35,13 @@ namespace bitsery { struct BasicBufferReader { using BufferType = typename Config::BufferType; - using ValueType = typename details::BufferContainerTraits::TValue; + using ValueType = typename details::ContainerTraits::TValue; using BufferIteratorType = typename details::BufferContainerTraits::TIterator; using ScratchType = typename details::SCRATCH_TYPE::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(), "Config::BufferValueType must be unsigned"); static_assert(std::is_unsigned(), "Config::BufferScrathType must be unsigned"); @@ -52,7 +51,7 @@ namespace bitsery { } BasicBufferReader(BufferRange range) - :BasicBufferReader(std::addressof(*range.begin()), std::addressof(*range.end())) { + :BasicBufferReader(range.begin(), range.end()) { static_assert(std::is_same< typename std::iterator_traits::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(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(error)); + return _bufferContext.setError(error); } void beginSession() { @@ -149,35 +142,21 @@ namespace bitsery { } private: - ValueType* _pos; - ValueType* _end; + details::ReadBufferContext _bufferContext; ScratchType m_scratch{}; size_t m_scratchBits{}; typename std::conditional, ValueType*>, + details::BufferSessionsReader, details::ReadBufferContext>, details::DisabledBufferSessionsReader>::type _session; template void directRead(T *v, size_t count) { static_assert(!std::is_const::value, ""); - const auto bytesCount = sizeof(T) * count; - - if (std::distance(_pos, _end) >= static_cast::TDifference>(bytesCount)) { - - std::memcpy(reinterpret_cast(v), _pos, bytesCount); - _pos += bytesCount; - - //swap each byte if nessesarry - _swapDataBits(v, count, std::integral_constant{}); - } else { - //set everything to zeros - std::memset(v, 0, bytesCount); - - if (getError() == BufferReaderError::NO_ERROR) - setError(BufferReaderError::BUFFER_OVERFLOW); - } + _bufferContext.read(reinterpret_cast(v), sizeof(T) * count); + //swap each byte if nessesarry + _swapDataBits(v, count, std::integral_constant{}); } template diff --git a/include/bitsery/buffer_writer.h b/include/bitsery/buffer_writer.h index 5523daa..ee78192 100644 --- a/include/bitsery/buffer_writer.h +++ b/include/bitsery/buffer_writer.h @@ -95,9 +95,9 @@ namespace bitsery { template struct BasicBufferWriter { using BufferType = typename Config::BufferType; - using ValueType = typename details::BufferContainerTraits::TValue; + using ValueType = typename details::ContainerTraits::TValue; using ScratchType = typename details::SCRATCH_TYPE::type; - using BufferContext = details::WriteBufferContext::isResizable>; + using BufferContext = details::WriteBufferContext::isResizable>; explicit BasicBufferWriter(BufferType &buffer) : _bufferContext{buffer} diff --git a/include/bitsery/common.h b/include/bitsery/common.h index 8eefb26..ab68173 100644 --- a/include/bitsery/common.h +++ b/include/bitsery/common.h @@ -25,6 +25,7 @@ #define BITSERY_COMMON_H #include +#include "traits/vector.h" namespace bitsery { diff --git a/include/bitsery/deserializer.h b/include/bitsery/deserializer.h index ac00049..eed29e8 100644 --- a/include/bitsery/deserializer.h +++ b/include/bitsery/deserializer.h @@ -50,7 +50,8 @@ namespace bitsery { template void object(T &&obj) { - details::SerializeFunction::invoke(*this, std::forward(obj)); + using TValue = typename std::decay::type; + details::SerializeFunction::invoke(*this, std::forward(obj)); } template @@ -58,25 +59,25 @@ namespace bitsery { fnc(std::forward(obj)); }; + /* + * functionality, that enables simpler serialization syntax, by including additional header + */ + template + void archive(T &&head, TArgs &&... tail) { + //serialize object + details::ArchiveFunction::invoke(*this, std::forward(head)); + //expand other elements + archive(std::forward(tail)...); + } + /* * value overloads */ - template::value>::type * = nullptr> + template::value>::type * = nullptr> void value(T &v) { - static_assert(std::numeric_limits::is_iec559, ""); - _reader.template readBytes(reinterpret_cast &>(v)); - } - - template::value>::type * = nullptr> - void value(T &v) { - using UT = typename std::underlying_type::type; - _reader.template readBytes(reinterpret_cast(v)); - } - - template::value>::type * = nullptr> - void value(T &v) { - _reader.template readBytes(v); + using TValue = typename details::IntegralFromFundamental::TValue; + _reader.template readBytes(reinterpret_cast(v)); } /* @@ -84,29 +85,26 @@ namespace bitsery { */ template - void ext(T &obj, Ext &&extension, Fnc &&fnc) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportLambdaOverload, + void ext(T &obj, const Ext &extension, Fnc &&fnc) { + static_assert(details::ExtensionTraits::SupportLambdaOverload, "extension doesn't support overload with lambda"); extension.deserialize(*this, _reader, obj, std::forward(fnc)); }; template - void ext(T &obj, Ext &&extension) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportValueOverload, + void ext(T &obj, const Ext &extension) { + static_assert(details::ExtensionTraits::SupportValueOverload, "extension doesn't support overload with `value`"); - using ExtVType = typename details::ExtensionTraits::TValue; + using ExtVType = typename details::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; extension.deserialize(*this, _reader, obj, [this](VType &v) { value(v); }); }; template - void ext(T &obj, Ext &&extension) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportObjectOverload, + void ext(T &obj, const Ext &extension) { + static_assert(details::ExtensionTraits::SupportObjectOverload, "extension doesn't support overload with `object`"); - using ExtVType = typename details::ExtensionTraits::TValue; + using ExtVType = typename details::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; extension.deserialize(*this, _reader, obj, [this](VType &v) { object(v); }); }; @@ -135,42 +133,21 @@ namespace bitsery { template void text(T &str, size_t maxSize) { - static_assert(details::TextTraits::isResizable, + static_assert(details::ContainerTraits::isResizable, "use text(T&) overload without `maxSize` for static containers"); - size_t size; - details::readSize(_reader, size, maxSize); - details::TextTraits::resize(str, size); - auto begin = std::begin(str); - auto end = std::next(begin, size); - procContainer(begin, end, std::true_type{}); - //null terminated character at the end - *end = {}; + size_t length; + details::readSize(_reader, length, maxSize); + details::ContainerTraits::resize(str, length + (details::TextTraits::addNUL ? 1u : 0u)); + procText(str, length); } template void text(T &str) { - static_assert(!details::TextTraits::isResizable, + static_assert(!details::ContainerTraits::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(std::distance(begin, containerEnd) - 1)); - //end of string, not en - auto end = std::next(begin, size); - procContainer(std::begin(str), std::end(str), std::true_type{}); - //null terminated character at the end - *end = {}; - } - - template - void text(T (&str)[N]) { - size_t size; - details::readSize(_reader, size, N - 1); - auto first = std::begin(str); - procContainer(first, std::next(first, size), std::true_type{}); - //null-terminated string - str[size] = {}; + size_t length; + details::readSize(_reader, length, details::ContainerTraits::size(str)); + procText(str, length); } /* @@ -197,7 +174,7 @@ namespace bitsery { size_t size{}; details::readSize(_reader, size, maxSize); details::ContainerTraits::resize(obj, size); - procContainer(std::begin(obj), std::end(obj), std::false_type{}); + procContainer(std::begin(obj), std::end(obj), std::integral_constant::isContiguous>{}); } template @@ -223,7 +200,7 @@ namespace bitsery { static_assert(!details::ContainerTraits::isResizable, "use container(T&, size_t) overload with `maxSize` for dynamic containers"); static_assert(VSIZE > 0, ""); - procContainer(std::begin(obj), std::end(obj), std::false_type{}); + procContainer(std::begin(obj), std::end(obj), std::integral_constant::isContiguous>{}); } template @@ -233,23 +210,6 @@ namespace bitsery { procContainer(std::begin(obj), std::end(obj)); } - //c-style array overloads - - template - void container(T (&arr)[N], Fnc &&fnc) { - procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); - } - - template - void container(T (&arr)[N]) { - procContainer(std::begin(arr), std::end(arr), std::true_type{}); - } - - template - void container(T (&arr)[N]) { - procContainer(std::begin(arr), std::end(arr)); - } - void align() { _reader.align(); } @@ -289,14 +249,14 @@ namespace bitsery { template void text4b(T &str, size_t maxSize) { text<4>(str, maxSize); } - template - void text1b(T (&str)[N]) { text<1>(str); } + template + void text1b(T &str) { text<1>(str); } - template - void text2b(T (&str)[N]) { text<2>(str); } + template + void text2b(T &str) { text<2>(str); } - template - void text4b(T (&str)[N]) { text<4>(str); } + template + void text4b(T &str) { text<4>(str); } template void container1b(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } @@ -322,19 +282,6 @@ namespace bitsery { template void container8b(T &&obj) { container<8>(std::forward(obj)); } - - template - void container1b(T (&arr)[N]) { container<1>(arr); } - - template - void container2b(T (&arr)[N]) { container<2>(arr); } - - template - void container4b(T (&arr)[N]) { container<4>(arr); } - - template - void container8b(T (&arr)[N]) { container<8>(arr); } - private: BasicBufferReader &_reader; void* _context; @@ -351,8 +298,10 @@ namespace bitsery { //true_type means, that we can copy whole buffer template void procContainer(It first, It last, std::true_type) { + using TValue = typename std::decay::type; + using TIntegral = typename details::IntegralFromFundamental::TValue; if (first != last) - _reader.template readBuffer(&(*first), std::distance(first, last)); + _reader.template readBuffer(reinterpret_cast(&(*first)), std::distance(first, last)); }; //process by calling functions @@ -369,6 +318,17 @@ namespace bitsery { object(*first); }; + template + 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(begin, end, std::integral_constant::isContiguous>{}); + //null terminated character at the end + if (details::TextTraits::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 diff --git a/include/bitsery/details/buffer_common.h b/include/bitsery/details/buffer_common.h index a5fdcb3..22917ba 100644 --- a/include/bitsery/details/buffer_common.h +++ b/include/bitsery/details/buffer_common.h @@ -140,8 +140,8 @@ namespace bitsery { template struct DisabledBufferSessionsReader { - template - DisabledBufferSessionsReader(TReader& , TIterator& , TIterator& ) { + template + DisabledBufferSessionsReader(TReader& , TBufferContext& ) { } void begin() { @@ -208,38 +208,42 @@ namespace bitsery { std::stack _sessionIndex; }; - template + template 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(std::distance(_begin, _end)); + auto currPos = static_cast(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 _sessions{}; std::vector::iterator _nextSessionIt{}; std::stack _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(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 class WriteBufferContext { public: - using TValue = typename BufferContainerTraits::TValue; + using TValue = typename ContainerTraits::TValue; using TIterator = typename BufferContainerTraits::TIterator; - using TDifference = typename BufferContainerTraits::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(size)); - memcpy(_outIt, data, size); + auto tmp = _outIt; _outIt += size; + assert(std::distance(_outIt, _end) >= 0); + memcpy(std::addressof(*tmp), data, size); } BufferRange getWrittenRange() const { - auto begin = std::begin(_buffer); - return BufferRange{begin, std::next(begin, _outIt - std::addressof(*begin))}; + return BufferRange{std::begin(_buffer), _outIt}; } private: Buffer &_buffer; - TValue* _outIt; - TValue* _end; + TIterator _outIt; + TIterator _end; }; template class WriteBufferContext { public: - using TValue = typename BufferContainerTraits::TValue; + using TValue = typename ContainerTraits::TValue; using TIterator = typename BufferContainerTraits::TIterator; - using TDifference = typename BufferContainerTraits::TDifference; explicit WriteBufferContext(Buffer &buffer) : _buffer{buffer} { + //resize buffer immediately, because we need output iterator at valid position + if (ContainerTraits::size(buffer) == 0u) { + BufferContainerTraits::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::increaseBufferSize(_buffer); //restore iterators - getIterators(pos); + getIterators(static_cast(pos)); write(data, size); } } BufferRange getWrittenRange() const { - auto begin = std::begin(_buffer); - return BufferRange{begin, std::next(begin, _outIt - std::addressof(*begin))}; + return BufferRange{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 + class ReadBufferContext { + public: + using TValue = typename ContainerTraits::TValue; + using TIterator = typename BufferContainerTraits::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(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(error)); + } + + bool isCompletedSuccessfully() const { + return _pos == _end; + } + + TIterator _pos; + TIterator _end; }; } diff --git a/include/bitsery/details/flexible_common.h b/include/bitsery/details/flexible_common.h new file mode 100644 index 0000000..096eb5e --- /dev/null +++ b/include/bitsery/details/flexible_common.h @@ -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::TValue>::value + && details::ContainerTraits::isResizable + >::type * = nullptr> + void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits::max()) { + using TValue = typename details::ContainerTraits::TValue; + s.template container(c, maxSize); + } + + template::TValue>::value + && details::ContainerTraits::isResizable + >::type * = nullptr> + void processContainer(S &s, T &c, size_t maxSize = std::numeric_limits::max()) { + s.container(c, maxSize); + } + + template::TValue>::value + && !details::ContainerTraits::isResizable + >::type * = nullptr> + void processContainer(S &s, T &c) { + using TValue = typename details::ContainerTraits::TValue; + s.template container(c); + } + + template::TValue>::value + && !details::ContainerTraits::isResizable + >::type * = nullptr> + void processContainer(S &s, T &c) { + s.container(c); + } + + //overloads for text processing to apply maxSize + + template::isResizable>::type * = nullptr> + void processText(S &s, T &c, size_t maxSize = std::numeric_limits::max()) { + using TValue = typename details::ContainerTraits::TValue; + s.template text(c, maxSize); + } + + template::isResizable>::type * = nullptr> + void processText(S &s, T &c) { + using TValue = typename details::ContainerTraits::TValue; + s.template text(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 + struct CArray : public ArchiveWrapperFnc { + CArray(T (&data_)[N]) : data{data_} {}; + T (&data)[N]; + }; + + template + void serialize(S &s, CArray &str) { + processText(s, str.data); + } + + template + void serialize(S &s, CArray &obj) { + processContainer(s, obj.data); + } + + //used to set max container size + template + struct MaxSize : public ArchiveWrapperFnc { + MaxSize(T &data_, size_t maxSize_) : data{data_}, maxSize{maxSize_} {}; + T &data; + size_t maxSize; + }; + + template + void serialize(S &s, const MaxSize &ms) { + processContainer(s, ms.data, ms.maxSize); + }; + + } +} + +#endif //BITSERY_DETAILS_FLEXIBLE_COMMON_H diff --git a/include/bitsery/details/serialization_common.h b/include/bitsery/details/serialization_common.h index 9fa68fd..e9b874f 100644 --- a/include/bitsery/details/serialization_common.h +++ b/include/bitsery/details/serialization_common.h @@ -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 + static auto serialize(S &s, T &obj) -> decltype(obj.serialize(s)) { + obj.serialize(s); + } + }; + namespace details { - template - struct SAME_SIZE_UNSIGNED_TYPE { - using type = typename std::make_unsigned::type; + //used for extensions, when extension TValue = void + struct DummyType { + }; + +/* + * this includes all integral types floats and enums(except bool) + */ + template + struct IsFundamentalType : std::integral_constant::value + || std::is_floating_point::value + || std::is_integral::value> { + }; + + template + struct IntegralFromFundamental { + using TValue = T; }; template - struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { - using type = typename std::make_unsigned::type>::type; + struct IntegralFromFundamental::value>::type> { + using TValue = typename std::underlying_type::type; }; template - struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { - using type = typename std::conditional::value, uint32_t, uint64_t>::type; + struct IntegralFromFundamental::value>::type> { + using TValue = typename std::conditional::value, uint32_t, uint64_t>::type; }; template - using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE::type; + struct UnsignedFromFundamental { + using type = typename std::make_unsigned::TValue>::type; + }; + + template + using SAME_SIZE_UNSIGNED = typename UnsignedFromFundamental::type; + /* * functions for object serialization @@ -54,28 +83,62 @@ namespace bitsery { template struct SerializeFunction { + static void invoke(S &s, T &v) { static_assert(!std::is_void::value, - "\nPlease define 'serialize' function for your type:\n" + "\nPlease define 'serialize' function for your type (inside or outside of class):\n" " template\n" - " void serialize(S& s, & o)\n" + " void serialize(S& s)\n" " {\n" " ...\n" " }\n"); } }; + //check for serialize(s,o) support template struct SerializeFunction(), std::declval()))>::value + std::is_same(), std::declval()))>::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 + struct SerializeFunction(), std::declval()))>::value + >::type> { + + static void invoke(S &s, T &v) { + Access::serialize(s, v); + } + }; + + +/* + * functions for object serialization + */ + + template + struct ArchiveFunction { + + static void invoke(S &s, T &v) { + static_assert(!std::is_void::value, + "\nPlease include 'flexible.h' to use 'archive' function:\n"); + } + }; + + template + struct ArchiveFunction(), std::declval()))>::value + >::type> { + + static void invoke(S &s, T &&obj) { + archiveProcess(s, std::forward(obj)); + } }; /* diff --git a/include/bitsery/details/traits.h b/include/bitsery/details/traits.h index 060febd..065478a 100644 --- a/include/bitsery/details/traits.h +++ b/include/bitsery/details/traits.h @@ -24,20 +24,10 @@ #define BITSERY_DETAILS_TRAITS_H #include -#include namespace bitsery { namespace details { - /* - * helper traits that is used internaly, or by other traits - */ - template - struct IsResizable : std::false_type {}; - - template - struct IsResizable ().resize(1u), 0)> : std::true_type {}; - /* * core library traits, used to extend library for custom types */ @@ -63,100 +53,88 @@ namespace bitsery { template 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::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::value, + "Define ContainerTraits or include from to use as container"); } - //get container size static size_t size(const T& container) { - return container.size(); + static_assert(std::is_void::value, + "Define ContainerTraits or include from to use as container"); + return 0u; } - }; //specialization for C style array template struct ContainerTraits { using TValue = T; - static constexpr bool isResizable = IsResizable::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 - 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::type; - return std::char_traits::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 - 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(std::basic_string& 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& container) { + struct ContainerTraits> { + using TValue = T; + static constexpr bool isResizable = false; + static constexpr bool isContiguous = true; + static size_t size(const std::initializer_list& container) { return container.size(); } }; + + //traits for text, default adds null-terminated character at the end + template + 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::value, + "Define TextTraits or include from to use as text"); + return 0u; + } + }; + //traits only for buffer reader/writer template - struct BufferContainerTraits: public ContainerTraits { + 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::value, + "Define BufferContainerTraits or include from to use as buffer"); } - using TDifference = typename T::difference_type; - using TIterator = typename T::iterator; + using TIterator = void; + }; + + //specialization for c-style buffer + template + struct BufferContainerTraits { + using TIterator = T*; }; } diff --git a/include/bitsery/flexible.h b/include/bitsery/flexible.h new file mode 100644 index 0000000..746cae2 --- /dev/null +++ b/include/bitsery/flexible.h @@ -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 + void archiveProcessImpl(S &s, T &&head, std::true_type) { + s.object(std::forward(head)); + }; + + //overload when T is rvalue type, only allowable for behaviour modifying functions for deserializer + template + void archiveProcessImpl(S &s, T &&head, std::false_type) { + static_assert(std::is_base_of::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 + void archiveProcess(S &s, T &&head) { + flexible::archiveProcessImpl(s, std::forward(head), std::is_reference{}); + } + + //wrapper functions that enables to serialize as container or string + template + flexible::CArray asText(T (&str)[N]) { + return {str}; + }; + + template + flexible::CArray asContainer(T (&obj)[N]) { + return {obj}; + }; + + template + flexible::MaxSize maxSize(T& obj, size_t max) { + return {obj, max}; + } + + +//define serialize function for fundamental types + template + void serialize(S &s, bool &v) { + s.boolByte(v); + } + + template::value>::type * = nullptr> + void serialize(S &s, T &v) { + s.template value(v); + }; + +//define serialization for c-style container + + //if array is integral type, specify explicitly how to process: as text or container + template::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::value>::type * = nullptr> + void serialize(S &s, T (&obj)[N]) { + s.container(obj); + }; + +} + +#endif //BITSERY_FLEXIBLE_H diff --git a/include/bitsery/flexible/array.h b/include/bitsery/flexible/array.h new file mode 100644 index 0000000..4fe0792 --- /dev/null +++ b/include/bitsery/flexible/array.h @@ -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 + void serialize(S &s, std::array &obj) { + flexible::processContainer(s, obj); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_ARRAY_H diff --git a/include/bitsery/flexible/list.h b/include/bitsery/flexible/list.h new file mode 100644 index 0000000..4d469c6 --- /dev/null +++ b/include/bitsery/flexible/list.h @@ -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 + void serialize(S &s, std::list &obj) { + flexible::processContainer(s, obj); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_LIST_H diff --git a/include/bitsery/flexible/map.h b/include/bitsery/flexible/map.h new file mode 100644 index 0000000..333d192 --- /dev/null +++ b/include/bitsery/flexible/map.h @@ -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 +#include "../ext/container_map.h" + +namespace bitsery { + template + void serialize(S &s, std::map &obj) { + using TKey = typename std::map::key_type; + using TValue = typename std::map::mapped_type; + s.ext(obj, ext::ContainerMap{std::numeric_limits::max()}, + [&s](TKey& key, TValue& value) { + s.object(key); + s.object(value); + }); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_MAP_H diff --git a/include/bitsery/flexible/string.h b/include/bitsery/flexible/string.h new file mode 100644 index 0000000..2b833b3 --- /dev/null +++ b/include/bitsery/flexible/string.h @@ -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 + void serialize(S &s, std::basic_string &str) { + flexible::processContainer(s, str); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_STRING_H diff --git a/include/bitsery/flexible/unordered_map.h b/include/bitsery/flexible/unordered_map.h new file mode 100644 index 0000000..d0383c0 --- /dev/null +++ b/include/bitsery/flexible/unordered_map.h @@ -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 +#include "../ext/container_map.h" + +namespace bitsery { + template + void serialize(S &s, std::unordered_map &obj) { + using TKey = typename std::unordered_map::key_type; + using TValue = typename std::unordered_map::mapped_type; + s.ext(obj, ext::ContainerMap{std::numeric_limits::max()}, + [&s](TKey& key, TValue& value) { + s.object(key); + s.object(value); + }); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_UNORDERED_MAP_H diff --git a/include/bitsery/flexible/vector.h b/include/bitsery/flexible/vector.h new file mode 100644 index 0000000..207e00f --- /dev/null +++ b/include/bitsery/flexible/vector.h @@ -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 + void serialize(S &s, std::vector &obj) { + flexible::processContainer(s, obj); + } +} + +#endif //BITSERY_FLEXIBLE_TYPE_VECTOR_H diff --git a/include/bitsery/serializer.h b/include/bitsery/serializer.h index e70ad11..8d4f28d 100644 --- a/include/bitsery/serializer.h +++ b/include/bitsery/serializer.h @@ -30,9 +30,6 @@ namespace bitsery { - - - template class BasicSerializer { public: @@ -59,24 +56,25 @@ namespace bitsery { fnc(const_cast(obj)); }; + /* + * functionality, that enables simpler serialization syntax, by including additional header + */ + template + void archive(T &&head, TArgs &&... tail) { + //serialize object + details::ArchiveFunction::invoke(*this, std::forward(head)); + //expand other elements + archive(std::forward(tail)...); + } + /* * value overloads */ - template::value>::type * = nullptr> + template::value>::type * = nullptr> void value(const T &v) { - static_assert(std::numeric_limits::is_iec559, ""); - _writter.template writeBytes(reinterpret_cast &>(v)); - } - - template::value>::type * = nullptr> - void value(const T &v) { - _writter.template writeBytes(reinterpret_cast::type &>(v)); - } - - template::value>::type * = nullptr> - void value(const T &v) { - _writter.template writeBytes(v); + using TValue = typename details::IntegralFromFundamental::TValue; + _writter.template writeBytes(reinterpret_cast(v)); } /* @@ -84,29 +82,26 @@ namespace bitsery { */ template - void ext(const T &obj, Ext &&extension, Fnc &&fnc) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportLambdaOverload, + void ext(const T &obj, const Ext &extension, Fnc &&fnc) { + static_assert(details::ExtensionTraits::SupportLambdaOverload, "extension doesn't support overload with lambda"); extension.serialize(*this, _writter, obj, std::forward(fnc)); }; template - void ext(const T &obj, Ext &&extension) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportValueOverload, + void ext(const T &obj, const Ext &extension) { + static_assert(details::ExtensionTraits::SupportValueOverload, "extension doesn't support overload with `value`"); - using ExtVType = typename details::ExtensionTraits::TValue; + using ExtVType = typename details::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; extension.serialize(*this, _writter, obj, [this](VType &v) { value(v); }); }; template - void ext(const T &obj, Ext &&extension) { - using ExtType = typename std::decay::type; - static_assert(details::ExtensionTraits::SupportObjectOverload, + void ext(const T &obj, const Ext &extension) { + static_assert(details::ExtensionTraits::SupportObjectOverload, "extension doesn't support overload with `object`"); - using ExtVType = typename details::ExtensionTraits::TValue; + using ExtVType = typename details::ExtensionTraits::TValue; using VType = typename std::conditional::value, details::DummyType, ExtVType>::type; extension.serialize(*this, _writter, obj, [this](VType &v) { object(v); }); }; @@ -129,37 +124,16 @@ namespace bitsery { template void text(const T &str, size_t maxSize) { - static_assert(details::TextTraits::isResizable, + static_assert(details::ContainerTraits::isResizable, "use text(const T&) overload without `maxSize` for static container"); - auto size = details::TextTraits::length(str); - //size can be equal to maxSize - assert(size <= maxSize); - details::writeSize(_writter, size); - auto begin = std::begin(str); - procContainer(begin, std::next(begin, size), std::true_type{}); + procText(str, maxSize); } template void text(const T &str) { - static_assert(!details::TextTraits::isResizable, + static_assert(!details::ContainerTraits::isResizable, "use text(const T&, size_t) overload with `maxSize` for dynamic containers"); - auto size = details::TextTraits::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(begin, std::next(begin, size), std::true_type{}); - } - - template - void text(const T (&str)[N]) { - auto size = details::TextTraits::length(str); - assert(size < N); - details::writeSize(_writter, size); - auto begin = std::begin(str); - procContainer(begin, std::next(begin, size), std::true_type{}); + procText(str, details::ContainerTraits::size(str)); } /* @@ -187,8 +161,8 @@ namespace bitsery { auto size = details::ContainerTraits::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(std::begin(obj), std::end(obj), std::false_type{}); + + procContainer(std::begin(obj), std::end(obj), std::integral_constant::isContiguous>{}); } template @@ -215,8 +189,7 @@ namespace bitsery { static_assert(!details::ContainerTraits::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(std::begin(obj), std::end(obj), std::false_type{}); + procContainer(std::begin(obj), std::end(obj), std::integral_constant::isContiguous>{}); } template @@ -226,24 +199,6 @@ namespace bitsery { procContainer(std::begin(obj), std::end(obj)); } - //c-style array overloads - - template - void container(const T (&arr)[N], Fnc &&fnc) { - procContainer(std::begin(arr), std::end(arr), std::forward(fnc)); - } - - template - void container(const T (&arr)[N]) { - static_assert(VSIZE > 0, ""); - procContainer(std::begin(arr), std::end(arr), std::true_type{}); - } - - template - void container(const T (&arr)[N]) { - procContainer(std::begin(arr), std::end(arr)); - } - void align() { _writter.align(); } @@ -283,14 +238,14 @@ namespace bitsery { template void text4b(const T &str, size_t maxSize) { text<4>(str, maxSize); } - template - void text1b(const T (&str)[N]) { text<1, T, N>(str); } + template + void text1b(const T &str) { text<1>(str); } - template - void text2b(const T (&str)[N]) { text<2, T, N>(str); } + template + void text2b(const T &str) { text<2>(str); } - template - void text4b(const T (&str)[N]) { text<4, T, N>(str); } + template + void text4b(const T &str) { text<4>(str); } template void container1b(T &&obj, size_t maxSize) { container<1>(std::forward(obj), maxSize); } @@ -316,18 +271,6 @@ namespace bitsery { template void container8b(T &&obj) { container<8>(std::forward(obj)); } - template - void container1b(const T (&arr)[N]) { container<1>(arr); } - - template - void container2b(const T (&arr)[N]) { container<2>(arr); } - - template - void container4b(const T (&arr)[N]) { container<4>(arr); } - - template - void container8b(const T (&arr)[N]) { container<8>(arr); } - private: BasicBufferWriter &_writter; void* _context; @@ -344,7 +287,10 @@ namespace bitsery { //true_type means, that we can copy whole buffer template void procContainer(It first, It last, std::true_type) { - _writter.template writeBuffer(&(*first), std::distance(first, last)); + using TValue = typename std::decay::type; + using TIntegral = typename details::IntegralFromFundamental::TValue; + if (first != last) + _writter.template writeBuffer(reinterpret_cast(&(*first)), std::distance(first, last)); }; //process by calling functions @@ -356,6 +302,16 @@ namespace bitsery { } }; + //process text, + template + void procText(const T& str, size_t maxSize) { + auto length = details::TextTraits::length(str); + assert((length + (details::TextTraits::addNUL ? 1u : 0u)) <= maxSize); + details::writeSize(_writter, length); + auto begin = std::begin(str); + procContainer(begin, std::next(begin, length), std::integral_constant::isContiguous>{}); + }; + //process object types template void procContainer(It first, It last) { @@ -373,6 +329,10 @@ namespace bitsery { } + //dummy function, that stops archive variadic arguments expansion + void archive() { + } + }; //helper type diff --git a/include/bitsery/traits/array.h b/include/bitsery/traits/array.h new file mode 100644 index 0000000..cd9513a --- /dev/null +++ b/include/bitsery/traits/array.h @@ -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 + +namespace bitsery { + + namespace details { + template + struct ContainerTraits> + :public StdContainer, false, true> {}; + + template + struct BufferContainerTraits> + :public StdContainerForBuffer> {}; + } + +} + +#endif //BITSERY_TYPE_TRAITS_ARRAY_H diff --git a/include/bitsery/traits/vector.h b/include/bitsery/traits/vector.h new file mode 100644 index 0000000..d065f46 --- /dev/null +++ b/include/bitsery/traits/vector.h @@ -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 + +namespace bitsery { + + namespace details { + template + struct ContainerTraits> + :public StdContainer, true, true> {}; + + //bool vector is not contiguous, do not copy it directly to buffer + template + struct ContainerTraits> + :public StdContainer, true, false> {}; + + template + struct BufferContainerTraits> + :public StdContainerForBuffer> {}; + + } + +} + +#endif //BITSERY_TRAITS_VECTOR_H diff --git a/tests/buffer_endianness.cpp b/tests/buffer_endianness.cpp index 091b77a..831cef9 100644 --- a/tests/buffer_endianness.cpp +++ b/tests/buffer_endianness.cpp @@ -145,7 +145,7 @@ struct IntegralUnsignedTypes { TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedByEndianness) { //fill initial values - static_assert(sizeof(bitsery::details::BufferContainerTraits::TValue) == 1, "currently only 1 byte size, value size is supported"); + static_assert(sizeof(bitsery::details::ContainerTraits::TValue) == 1, "currently only 1 byte size, value size is supported"); //fill initial values constexpr IntegralUnsignedTypes src { 0x0000334455667788,//bits 19 diff --git a/tests/buffer_reading_errors.cpp b/tests/buffer_reading_errors.cpp index e1dfc50..f0a11c9 100644 --- a/tests/buffer_reading_errors.cpp +++ b/tests/buffer_reading_errors.cpp @@ -22,6 +22,7 @@ #include #include "serialization_test_utils.h" +#include using testing::Eq; using bitsery::BufferWriter; diff --git a/tests/buffer_writing.cpp b/tests/buffer_writing.cpp index a1a87d6..94f334e 100644 --- a/tests/buffer_writing.cpp +++ b/tests/buffer_writing.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include using testing::Eq; using testing::ContainerEq; diff --git a/tests/serialization_container.cpp b/tests/serialization_container.cpp index 3dd5c22..891cfb0 100644 --- a/tests/serialization_container.cpp +++ b/tests/serialization_container.cpp @@ -25,9 +25,12 @@ #include #include #include -#include -#include +// #include #include "serialization_test_utils.h" +#include +#include +#include + using testing::ContainerEq; diff --git a/tests/serialization_ext_container_map.cpp b/tests/serialization_ext_container_map.cpp index 0516a31..71cfa64 100644 --- a/tests/serialization_ext_container_map.cpp +++ b/tests/serialization_ext_container_map.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include using ContainerMap = bitsery::ext::ContainerMap; diff --git a/tests/serialization_ext_entropy.cpp b/tests/serialization_ext_entropy.cpp index 0918263..65e076c 100644 --- a/tests/serialization_ext_entropy.cpp +++ b/tests/serialization_ext_entropy.cpp @@ -24,8 +24,7 @@ #include #include "serialization_test_utils.h" #include -#include -#include +#include using namespace testing; diff --git a/tests/serialization_ext_growable.cpp b/tests/serialization_ext_growable.cpp index 059878c..c921b89 100644 --- a/tests/serialization_ext_growable.cpp +++ b/tests/serialization_ext_growable.cpp @@ -29,7 +29,6 @@ using namespace testing; using bitsery::ext::Growable; using Buffer = typename bitsery::DefaultConfig::BufferType; -using DiffType = typename bitsery::details::BufferContainerTraits::TDifference; struct DataV1 { int32_t v1; diff --git a/tests/serialization_objects.cpp b/tests/serialization_objects.cpp index c5b7213..287eef1 100644 --- a/tests/serialization_objects.cpp +++ b/tests/serialization_objects.cpp @@ -23,6 +23,8 @@ #include #include "serialization_test_utils.h" +#include +#include using testing::Eq; using testing::StrEq; diff --git a/tests/serialization_text.cpp b/tests/serialization_text.cpp index 78b3c77..22b5348 100644 --- a/tests/serialization_text.cpp +++ b/tests/serialization_text.cpp @@ -23,6 +23,8 @@ #include #include "serialization_test_utils.h" +#include + 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), ""); } \ No newline at end of file