buffer reading/writing improvements

This commit is contained in:
fraillt
2017-09-01 16:23:52 +03:00
parent fe2bccf28f
commit 241f1f2940
15 changed files with 424 additions and 357 deletions

View File

@@ -4,7 +4,11 @@
### Features
* now all serializer/deserializer functions return void, to avoid undefined behaviour for functions parameters evaluation when using method chaining. There was no benefits apart from *nicer* syntax, but could have undefined behaviour when building complex serialization flows.
* changed BufferWriter/Reader config, added *FixedBufferSize* bool parameter for *BufferWriter* for much better serializer performance (more than 50% improvement). Default is old behavour - using back_insert_iterator when writing.
* changed BufferWriter/Reader behaviour,
* after serialization, call *getWrittenRange* to get valid written range.
* BufferReader only has constructors with iterators.
* added *FixedBufferSize* config bool parameter for *BufferWriter* for better serializer performance (more than 50% improvement). Default is resizable buffer.
* BufferWritter for resizable buffer now always resize to *capacity* to avoid using *back_insert_iterator* for better performance.
* added **SERIALIZE_FRIEND** macro to be able to serialize private struct fields
* user friendly static_assert when trying to serialize object, that doesn't have **serialize** function defined.
* added **custom** function to override default behaviour for **object** serialization

View File

@@ -18,7 +18,7 @@ All cross-platform requirements are enforced at compile time, so serialized data
* Configurable endianess support.
* Advanced serialization features like value ranges and entrophy encoding.
* Easy to extend for types that requires different serialization and deserialization logic (e.g. pointers, or geometry compression).
* Error checking at runtime on deserialization, and asserts on serialization errors.
* No exceptions. Error checking at runtime for deserialization, and asserts on serialization.
## How to use it
This documentation comprises these parts:

View File

@@ -2,7 +2,7 @@ To get the most out of **Bitsery**, start with the [tutorial](tutorial/README.md
Once you're familiar with the library consider the following reference material.
Library design:
* [*valueN* instead of *value*](design/func_n.md)
* [*valueN* instead of *value*](design/function_n.md)
* [fundamental types](design/fundamental_types.md)
* [serializer/deserializer functions overloads](design/function_overload.md)
* [extending library functionality](design/extensions.md)
@@ -29,11 +29,6 @@ Tips and tricks:
Advanced topics:
FAQ:
* [Known limitations](limitations.md)
Other:
* [Why Bitsery?](why-bitsery.md)
* [Contributing](../CONTRIBUTING.md)

View File

@@ -2,14 +2,13 @@ The grand plan for this tutorial is to learn how to serialize/deserialize any ob
This tutorial will cover these main topics:
* [Hello World](hello_world.md) how to serialize a simple struct.
* [2 in 1](two_in_one.md) how to write one control flow for both, serialization and deserialization.
* [Composer](composition.md) how to make your type serializable by default or override default flow.
* [Squeeze Me!](compression.md) how to compress your data if you know what it stores.
* [Anything is Possible](extensions.md) how to extend library for your custom container, compress geometry and more.
* [Little or Big](endianness.md) how to change Endianness if you want best performance on PowerPC.
* [Hello World](hello_world.md) serialize a simple struct.
* [2 in 1](two_in_one.md) write one control flow for both: serialization and deserialization.
* [Composer](composition.md) efficiently compose complex serialization flows.
* [Squeeze Me!](compression.md) compress your data when you know what it stores.
* [Anything is Possible](extensions.md) extend library for custom container, compress geometry and more.
* [Little or Big](endianness.md) change endianness if you want best performance on PowerPC.
In order to successfully use the library you need c++14 compatible compiler. In theory you could also use c++11 compatible compiler, but c++14 generic lambdas really change the way you can work with this library, so all tutorial sections will asume that you use c++14 compatible compiler.
So without further ado lets start with [hello world](hello_world.md).

View File

@@ -34,9 +34,11 @@ namespace bitsery {
struct BasicBufferReader {
using BufferType = typename Config::BufferType;
using ValueType = typename BufferType::value_type;
using IteratorType = typename BufferType::iterator;
using ScratchType = typename details::SCRATCH_TYPE<ValueType>::type;
BasicBufferReader(const ValueType *data, size_t size) : _pos{data}, _end{data + size} {
BasicBufferReader(IteratorType begin, IteratorType end)
:_pos{begin}, _end{end} {
static_assert(std::is_unsigned<ValueType>(), "Config::BufferValueType must be unsigned");
static_assert(std::is_unsigned<ScratchType>(), "Config::BufferScrathType must be unsigned");
static_assert(sizeof(ValueType) * 2 == sizeof(ScratchType),
@@ -44,11 +46,8 @@ namespace bitsery {
static_assert(sizeof(ValueType) == 1, "currently only supported BufferValueType is 1 byte");
}
BasicBufferReader(typename BufferType::iterator begin, typename BufferType::iterator end)
: BasicBufferReader(std::addressof(*begin), std::distance(begin, end)) {}
explicit BasicBufferReader(const BufferType &buf) : BasicBufferReader(buf.data(), buf.size()) {
}
BasicBufferReader(BufferRange<IteratorType> range)
:BasicBufferReader(range.begin(), range.end()) {}
BasicBufferReader(const BasicBufferReader &) = delete;
@@ -117,8 +116,8 @@ namespace bitsery {
}
private:
const ValueType *_pos;
const ValueType *_end;
IteratorType _pos;
IteratorType _end;
template<typename T>
bool directRead(T *v, size_t count) {

View File

@@ -147,8 +147,8 @@ namespace bitsery {
}
}
size_t getWrittenBytesCount() const {
return _bufferContext.getWrittenBytesCount();
BufferRange<typename BufferType::iterator> getWrittenRange() const {
return _bufferContext.getWrittenRange();
}
private:

View File

@@ -27,6 +27,8 @@
#include "details/buffer_common.h"
#include <vector>
#include <list>
namespace bitsery {
struct DefaultConfig {

View File

@@ -27,6 +27,8 @@
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <utility>
#include <cassert>
namespace bitsery {
@@ -38,6 +40,14 @@ namespace bitsery {
BigEndian
};
template<typename I>
struct BufferRange : std::pair<I, I> {
using std::pair<I, I>::pair;
I begin() const { return this->first; }
I end() const { return this->second; }
};
namespace details {
template<typename T>
constexpr size_t BITS_SIZE = sizeof(T) << 3;
@@ -126,46 +136,66 @@ namespace bitsery {
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer},
_outIt{buffer.begin()} {
_outIt{buffer.begin()},
_end{buffer.end()}
{
}
void write(const ValueType *data, size_t size) {
assert(std::distance(_outIt, _end) >= static_cast<typename Buffer::difference_type>(size));
_outIt = std::copy_n(data, size, _outIt);
}
size_t getWrittenBytesCount() const {
return std::distance(_buffer.begin(), _outIt) * sizeof(ValueType);
BufferRange<IteratorType> getWrittenRange() const {
return BufferRange<IteratorType>{_buffer.begin(), _outIt};
}
private:
Buffer &_buffer;
IteratorType _outIt;
IteratorType _end;
};
template<typename Buffer>
class WriteBufferContext<Buffer, false> {
public:
using ValueType = typename Buffer::value_type;
using IteratorType = std::back_insert_iterator<Buffer>;
using IteratorType = typename Buffer::iterator;
explicit WriteBufferContext(Buffer &buffer)
: _buffer{buffer},
_outIt{std::back_insert_iterator<Buffer>(buffer)},
_initialSize{buffer.size()} {
: _buffer{buffer}
{
resizeToCapacity(0);
}
void write(const ValueType *data, size_t size) {
std::copy_n(data, size, _outIt);
if (std::distance(_outIt, _end) >= static_cast<typename Buffer::difference_type>(size)) {
_outIt = std::copy_n(data, size, _outIt);
} else {
//get current position before invalidating iterators
auto pos = std::distance(_buffer.begin(), _outIt);
//make dummy call to back insert iterator to resize buffer
*(std::back_insert_iterator<Buffer>(_buffer)) = {};
resizeToCapacity(pos);
write(data, size);
}
}
size_t getWrittenBytesCount() const {
return (std::distance(_buffer.begin(), _buffer.end()) - _initialSize) * sizeof(ValueType);
BufferRange<IteratorType> getWrittenRange() const {
return BufferRange<IteratorType>{_buffer.begin(), _outIt};
}
private:
void resizeToCapacity(typename Buffer::difference_type writePos) {
if (_buffer.capacity() != _buffer.size()) {
_buffer.resize(_buffer.capacity());
}
_end = _buffer.end();
_outIt = std::next(_buffer.begin(), writePos);
}
Buffer &_buffer;
IteratorType _outIt;
size_t _initialSize;
IteratorType _end;
};
}

View File

@@ -1,272 +0,0 @@
//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.
#include <gmock/gmock.h>
#include <bitsery/buffer_writer.h>
#include <bitsery/buffer_reader.h>
#include <list>
#include <bitset>
using testing::Eq;
using testing::ContainerEq;
using bitsery::BufferWriter;
using bitsery::BufferReader;
using Buffer = bitsery::DefaultConfig::BufferType;
struct IntegralTypes {
int64_t a;
uint32_t b;
int16_t c;
uint8_t d;
int8_t e;
int8_t f[2];
};
IntegralTypes getInitializedIntegralTypes() {
IntegralTypes data;
data.a = -4894541654564;
data.b = 94545646;
data.c = -8778;
data.d = 200;
data.e = -98;
data.f[0] = 43;
data.f[1] = -45;
return data;
}
void writeIntegralTypesToBuffer(BufferWriter& bw, const IntegralTypes& data) {
bw.writeBytes<4>(data.b);
bw.writeBytes<1>(data.f[0]);
bw.writeBytes<2>(data.c);
bw.writeBytes<1>(data.d);
bw.writeBytes<8>(data.a);
bw.writeBytes<1>(data.e);
bw.writeBytes<1>(data.f[1]);
}
TEST(BufferBytesOperations, WriteAndReadBytes) {
//setup data
auto data =getInitializedIntegralTypes();
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
writeIntegralTypesToBuffer(bw, data);
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(18));
//read from buffer
BufferReader br{buf};
IntegralTypes res{};
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[0]), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.readBytes<8>(res.a), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.e), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[1]), Eq(true));
//assert results
EXPECT_THAT(data.a, Eq(res.a));
EXPECT_THAT(data.b, Eq(res.b));
EXPECT_THAT(data.c, Eq(res.c));
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(BufferBytesOperations, BufferReaderUsingDataPlusSizeCtor) {
//setup data
auto data =getInitializedIntegralTypes();
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
writeIntegralTypesToBuffer(bw, data);
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(18));
//read from buffer
BufferReader br{buf.data(), buf.size()};
IntegralTypes res{};
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[0]), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.readBytes<8>(res.a), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.e), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[1]), Eq(true));
//assert results
EXPECT_THAT(data.a, Eq(res.a));
EXPECT_THAT(data.b, Eq(res.b));
EXPECT_THAT(data.c, Eq(res.c));
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(BufferBytesOperations, BufferReaderUsingIteratorsCtor) {
//setup data
auto data =getInitializedIntegralTypes();
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
writeIntegralTypesToBuffer(bw, data);
ASSERT_THAT(std::distance(buf.begin(), buf.end()), Eq(18));
//read from buffer
BufferReader br{buf.begin(), buf.end()};
IntegralTypes res{};
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[0]), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.readBytes<8>(res.a), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.e), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.f[1]), Eq(true));
//assert results
EXPECT_THAT(data.a, Eq(res.a));
EXPECT_THAT(data.b, Eq(res.b));
EXPECT_THAT(data.c, Eq(res.c));
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(BufferBytesOperations, ReadReturnsFalseIfNotEnoughBufferSize) {
//setup data
uint8_t a = 111;
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBytes<1>(a);
bw.writeBytes<1>(a);
bw.writeBytes<1>(a);
//read from buffer
BufferReader br{buf};
int16_t b;
int32_t c;
EXPECT_THAT(br.readBytes<4>(c), Eq(false));
EXPECT_THAT(br.readBytes<2>(b), Eq(true));
EXPECT_THAT(br.readBytes<2>(b), Eq(false));
EXPECT_THAT(br.readBytes<1>(a), Eq(true));
EXPECT_THAT(br.readBytes<1>(a), Eq(false));
EXPECT_THAT(br.readBytes<2>(b), Eq(false));
EXPECT_THAT(br.readBytes<4>(c), Eq(false));
}
TEST(BufferBytesOperations, ReadIsCompletedWhenAllBytesAreRead) {
//setup data
IntegralTypes data;
data.b = 94545646;
data.c = -8778;
data.d = 200;
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBytes<4>(data.b);
bw.writeBytes<2>(data.c);
bw.writeBytes<1>(data.d);
//read from buffer
BufferReader br{buf};
IntegralTypes res;
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.isCompleted(), Eq(false));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.isCompleted(), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(false));
EXPECT_THAT(br.isCompleted(), Eq(true));
BufferReader br1{buf};
EXPECT_THAT(br1.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br1.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br1.isCompleted(), Eq(false));
EXPECT_THAT(br1.readBytes<2>(res.c), Eq(false));
EXPECT_THAT(br1.isCompleted(), Eq(false));
EXPECT_THAT(br1.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br1.isCompleted(), Eq(true));
}
TEST(BufferBytesOperations, ReadWriteBufferFncCanAcceptSignedData) {
//setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE] {54,-4877,30067};
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBuffer<2>(src, DATA_SIZE);
bw.flush();
//read from buffer
BufferReader br1{buf};
int16_t dst[DATA_SIZE]{};
EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true));
EXPECT_THAT(dst, ContainerEq(src));
//read more than available
BufferReader br2{buf};
int16_t dstMore[DATA_SIZE+1]{};
EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false));
}
TEST(BufferBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) {
//setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE] {54,-4877,30067};
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBits(15u, 4);
bw.writeBuffer<2>(src, DATA_SIZE);
bw.writeBits(12u, 4);
bw.flush();
EXPECT_THAT(buf.size(), Eq(sizeof(src) + 1));
//read from buffer
BufferReader br1{buf};
int16_t dst[DATA_SIZE]{};
uint8_t tmp{};
br1.readBits(tmp, 4);
EXPECT_THAT(tmp, Eq(15));
EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true));
EXPECT_THAT(dst, ContainerEq(src));
br1.readBits(tmp, 4);
EXPECT_THAT(tmp, Eq(12));
//read more than available
BufferReader br2{buf};
br2.readBits(tmp, 4);
int16_t dstMore[DATA_SIZE+1]{};
EXPECT_THAT(tmp, Eq(15));
EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false));
}

View File

@@ -80,7 +80,7 @@ TEST(BufferEndianness, WhenWriteBytesThenBytesAreSwapped) {
bw.writeBytes<1>(src.e);
bw.flush();
//read from buffer using inverse endianness config
bitsery::BasicBufferReader<InverseEndiannessConfig> br{buf};
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
IntegralTypes res{};
br.readBytes<8>(res.a);
br.readBytes<4>(res.b);
@@ -106,7 +106,7 @@ TEST(BufferEndianness, WhenWriteBuffer1ByteValuesThenEndiannessIsIgnored) {
bw.writeBuffer<1>(src, SIZE);
bw.flush();
//read from buffer using inverse endianness config
bitsery::BasicBufferReader<InverseEndiannessConfig> br{buf};
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
br.readBuffer<1>(res, SIZE);
//result is identical, because we write separate values, of size 1byte, that requires no swapping
//check results
@@ -125,7 +125,7 @@ TEST(BufferEndianness, WhenWriteBufferMoreThan1ByteValuesThenValuesAreSwapped) {
bw.writeBuffer<2>(src, SIZE);
bw.flush();
//read from buffer using inverse endianness config
bitsery::BasicBufferReader<InverseEndiannessConfig> br{buf};
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
br.readBuffer<2>(res, SIZE);
//result is identical, because we write separate values, of size 1byte, that requires no swapping
//check results
@@ -169,7 +169,7 @@ TEST(BufferEndianness, WhenBufferValueTypeIs1ByteThenBitOperationsIsNotAffectedB
bw.writeBits(src.d, dBITS);
bw.flush();
//read from buffer using inverse endianness config
bitsery::BasicBufferReader<InverseEndiannessConfig> br{buf};
bitsery::BasicBufferReader<InverseEndiannessConfig> br{bw.getWrittenRange()};
IntegralUnsignedTypes res{};
br.readBits(res.a, aBITS);
br.readBits(res.b, bBITS);

View File

@@ -45,7 +45,9 @@ constexpr size_t getBits(T v) {
return bitsery::details::calcRequiredBits<T>({}, v);
};
TEST(BufferBitsOperations, WriteAndReadBits) {
// *** bits operations
TEST(BufferBitsAndBytesOperations, WriteAndReadBits) {
//setup data
constexpr IntegralUnsignedTypes data{
485454,//bits 19
@@ -71,10 +73,11 @@ TEST(BufferBitsOperations, WriteAndReadBits) {
bw.writeBits(data.d, dBITS);
bw.writeBits(data.e, eBITS);
bw.flush();
auto range = bw.getWrittenRange();
auto bytesCount = ((aBITS + bBITS + cBITS + dBITS + eBITS) / 8) +1 ;
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(bytesCount));
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(bytesCount));
//read from buffer
BufferReader br{buf};
BufferReader br{range};
IntegralUnsignedTypes res;
br.readBits(res.a, aBITS);
@@ -91,19 +94,7 @@ TEST(BufferBitsOperations, WriteAndReadBits) {
}
TEST(BufferBitsOperations, WhenFinishedFlushWriter) {
Buffer buf;
BufferWriter bw{buf};
bw.writeBits(3u, 2);
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(0));
bw.flush();
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
}
TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) {
TEST(BufferBitsAndBytesOperations, BufferSizeIsCountedPerByteNotPerBit) {
//setup data
//create and write to buffer
@@ -112,10 +103,11 @@ TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) {
bw.writeBits(7u,3);
bw.flush();
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
auto range = bw.getWrittenRange();
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(1));
//read from buffer
BufferReader br{buf};
BufferReader br{range};
uint16_t tmp;
EXPECT_THAT(br.readBits(tmp,4), Eq(true));
EXPECT_THAT(br.readBits(tmp,2), Eq(true));
@@ -123,16 +115,16 @@ TEST(BufferBitsOperations, BufferSizeIsCountedPerByteNotPerBit) {
EXPECT_THAT(br.readBits(tmp,2), Eq(false));
//part of next byte
BufferReader br1{buf};
BufferReader br1{range};
EXPECT_THAT(br1.readBits(tmp,2), Eq(true));
EXPECT_THAT(br1.readBits(tmp,7), Eq(false));
//bigger than byte
BufferReader br2{buf};
BufferReader br2{range};
EXPECT_THAT(br2.readBits(tmp,9), Eq(false));
}
TEST(BufferBitsOperations, ConsecutiveCallsToAlignHasNoEffect) {
TEST(BufferBitsAndBytesOperations, ConsecutiveCallsToAlignHasNoEffect) {
Buffer buf;
BufferWriter bw{buf};
@@ -148,7 +140,7 @@ TEST(BufferBitsOperations, ConsecutiveCallsToAlignHasNoEffect) {
bw.flush();
unsigned char tmp;
BufferReader br{buf};
BufferReader br{bw.getWrittenRange()};
EXPECT_THAT(br.readBits(tmp,2), Eq(true));
EXPECT_THAT(tmp, Eq(3u));
EXPECT_THAT(br.align(), Eq(true));
@@ -163,24 +155,7 @@ TEST(BufferBitsOperations, ConsecutiveCallsToAlignHasNoEffect) {
EXPECT_THAT(tmp, Eq(15u));
}
TEST(BufferBitsOperations, WhenAlignedFlushHasNoEffect) {
//setup data
//create and write to buffer
Buffer buf;
BufferWriter bw{buf};
bw.writeBits(3u, 2);
bw.align();
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
bw.flush();
EXPECT_THAT(std::distance(buf.begin(), buf.end()), Eq(1));
}
TEST(BufferBitsOperations, AlignMustWriteZerosBits) {
TEST(BufferBitsAndBytesOperations, AlignWritesZerosBits) {
//setup data
//create and write to buffer
@@ -190,16 +165,128 @@ TEST(BufferBitsOperations, AlignMustWriteZerosBits) {
//write 2 bits and align
bw.writeBits(3u, 2);
bw.align();
bw.flush();
unsigned char tmp;
BufferReader br1{buf};
BufferReader br1{bw.getWrittenRange()};
br1.readBits(tmp,2);
//read aligned bits
EXPECT_THAT(br1.readBits(tmp,6), Eq(true));
EXPECT_THAT(tmp, Eq(0));
BufferReader br2{buf};
BufferReader br2{bw.getWrittenRange()};
//read 2 bits
br2.readBits(tmp,2);
EXPECT_THAT(br2.align(), Eq(true));
}
// *** bytes operations
struct IntegralTypes {
int64_t a;
uint32_t b;
int16_t c;
uint8_t d;
int8_t e;
int8_t f[2];
};
TEST(BufferBitsAndBytesOperations, WriteAndReadBytes) {
//setup data
IntegralTypes data;
data.a = -4894541654564;
data.b = 94545646;
data.c = -8778;
data.d = 200;
data.e = -98;
data.f[0] = 43;
data.f[1] = -45;
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBytes<4>(data.b);
bw.writeBytes<2>(data.c);
bw.writeBytes<1>(data.d);
bw.writeBytes<8>(data.a);
bw.writeBytes<1>(data.e);
bw.writeBuffer<1>(data.f, 2);
bw.flush();
auto range = bw.getWrittenRange();
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(18));
//read from buffer
BufferReader br{range};
IntegralTypes res{};
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.readBytes<8>(res.a), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.e), Eq(true));
EXPECT_THAT(br.readBuffer<1>(res.f, 2), Eq(true));
//assert results
EXPECT_THAT(data.a, Eq(res.a));
EXPECT_THAT(data.b, Eq(res.b));
EXPECT_THAT(data.c, Eq(res.c));
EXPECT_THAT(data.d, Eq(res.d));
EXPECT_THAT(data.e, Eq(res.e));
EXPECT_THAT(data.f, ContainerEq(res.f));
}
TEST(BufferBitsAndBytesOperations, ReadWriteBufferFncCanAcceptSignedData) {
//setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE] {54,-4877,30067};
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBuffer<2>(src, DATA_SIZE);
bw.flush();
//read from buffer
BufferReader br1{bw.getWrittenRange()};
int16_t dst[DATA_SIZE]{};
EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true));
EXPECT_THAT(dst, ContainerEq(src));
//read more than available
BufferReader br2{bw.getWrittenRange()};
int16_t dstMore[DATA_SIZE+1]{};
EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false));
}
TEST(BufferBitsAndBytesOperations, ReadWriteBufferCanWorkOnUnalignedData) {
//setup data
constexpr size_t DATA_SIZE = 3;
int16_t src[DATA_SIZE] {54,-4877,30067};
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBits(15u, 4);
bw.writeBuffer<2>(src, DATA_SIZE);
bw.writeBits(12u, 4);
bw.flush();
auto range = bw.getWrittenRange();
EXPECT_THAT(std::distance(range.begin(), range.end()), Eq(sizeof(src) + 1));
//read from buffer
BufferReader br1{range};
int16_t dst[DATA_SIZE]{};
uint8_t tmp{};
br1.readBits(tmp, 4);
EXPECT_THAT(tmp, Eq(15));
EXPECT_THAT(br1.readBuffer<2>(dst, DATA_SIZE), Eq(true));
EXPECT_THAT(dst, ContainerEq(src));
br1.readBits(tmp, 4);
EXPECT_THAT(tmp, Eq(12));
//read more than available
BufferReader br2{range};
br2.readBits(tmp, 4);
int16_t dstMore[DATA_SIZE+1]{};
EXPECT_THAT(tmp, Eq(15));
EXPECT_THAT(br2.readBuffer<2>(dstMore, DATA_SIZE+1), Eq(false));
}

107
tests/buffer_reading.cpp Normal file
View File

@@ -0,0 +1,107 @@
//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.
#include <gmock/gmock.h>
#include <bitsery/buffer_writer.h>
#include <bitsery/buffer_reader.h>
#include <list>
#include <bitset>
using testing::Eq;
using testing::ContainerEq;
using bitsery::BufferWriter;
using bitsery::BufferReader;
using Buffer = bitsery::DefaultConfig::BufferType;
struct IntegralTypes {
int64_t a;
uint32_t b;
int16_t c;
uint8_t d;
int8_t e;
int8_t f[2];
};
TEST(BufferReading, ReadReturnsFalseIfNotEnoughBufferSize) {
//setup data
uint8_t a = 111;
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBytes<1>(a);
bw.writeBytes<1>(a);
bw.writeBytes<1>(a);
bw.flush();
//read from buffer
BufferReader br{bw.getWrittenRange()};
int16_t b;
int32_t c;
EXPECT_THAT(br.readBytes<4>(c), Eq(false));
EXPECT_THAT(br.readBytes<2>(b), Eq(true));
EXPECT_THAT(br.readBytes<2>(b), Eq(false));
EXPECT_THAT(br.readBytes<1>(a), Eq(true));
EXPECT_THAT(br.readBytes<1>(a), Eq(false));
EXPECT_THAT(br.readBytes<2>(b), Eq(false));
EXPECT_THAT(br.readBytes<4>(c), Eq(false));
}
TEST(BufferReading, ReadIsCompletedWhenAllBytesAreRead) {
//setup data
IntegralTypes data;
data.b = 94545646;
data.c = -8778;
data.d = 200;
//create and write to buffer
Buffer buf{};
BufferWriter bw{buf};
bw.writeBytes<4>(data.b);
bw.writeBytes<2>(data.c);
bw.writeBytes<1>(data.d);
bw.flush();
//read from buffer
BufferReader br{bw.getWrittenRange()};
IntegralTypes res;
EXPECT_THAT(br.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br.isCompleted(), Eq(false));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br.isCompleted(), Eq(true));
EXPECT_THAT(br.readBytes<1>(res.d), Eq(false));
EXPECT_THAT(br.isCompleted(), Eq(true));
BufferReader br1{bw.getWrittenRange()};
EXPECT_THAT(br1.readBytes<4>(res.b), Eq(true));
EXPECT_THAT(br1.readBytes<2>(res.c), Eq(true));
EXPECT_THAT(br1.isCompleted(), Eq(false));
EXPECT_THAT(br1.readBytes<2>(res.c), Eq(false));
EXPECT_THAT(br1.isCompleted(), Eq(false));
EXPECT_THAT(br1.readBytes<1>(res.d), Eq(true));
EXPECT_THAT(br1.isCompleted(), Eq(true));
}

115
tests/buffer_writing.cpp Normal file
View File

@@ -0,0 +1,115 @@
//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.
#include <gmock/gmock.h>
#include <bitsery/buffer_writer.h>
#include <bitsery/buffer_reader.h>
#include <bitsery/details/serialization_common.h>
using testing::Eq;
using testing::ContainerEq;
using bitsery::EndiannessType;
using bitsery::DefaultConfig;
using Buffer = bitsery::DefaultConfig::BufferType;
struct FixedBufferConfig {
static constexpr bitsery::EndiannessType NetworkEndianness = DefaultConfig::NetworkEndianness;
static constexpr bool FixedBufferSize = true;
using BufferType = std::array<uint8_t, 100>;
};
struct NonFixedBufferConfig: public DefaultConfig {
};
template <typename Config>
class BufferWriting:public testing::Test {
public:
using type = Config;
};
using BufferWritingConfigs = ::testing::Types<
NonFixedBufferConfig,
FixedBufferConfig>;
TYPED_TEST_CASE(BufferWriting, BufferWritingConfigs);
static constexpr size_t DATA_SIZE = 14u;
template <typename BW>
void writeData(BW& bw) {
uint16_t tmp1{45}, tmp2{6543}, tmp3{46533};
uint32_t tmp4{8979445}, tmp5{7987564};
bw.template writeBytes<2>(tmp1);
bw.template writeBytes<2>(tmp2);
bw.template writeBytes<2>(tmp3);
bw.template writeBytes<4>(tmp4);
bw.template writeBytes<4>(tmp5);
}
TYPED_TEST(BufferWriting, GetWrittenRangeReturnsIterators) {
using Config = typename TestFixture::type;
using Buffer = typename Config::BufferType;
Buffer buf{};
bitsery::BasicBufferWriter<Config> bw{buf};
writeData(bw);
bw.flush();
auto range = bw.getWrittenRange();
EXPECT_THAT(std::distance(range.begin(), range.end()), DATA_SIZE);
}
TYPED_TEST(BufferWriting, WhenWritingBitsThenFlushWriter) {
Buffer buf;
bitsery::BufferWriter bw{buf};
bw.writeBits(3u, 2);
auto range1 = bw.getWrittenRange();
bw.flush();
auto range2 = bw.getWrittenRange();
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(0));
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
}
TYPED_TEST(BufferWriting, WhenDataAlignedThenFlushHasNoEffect) {
using Config = typename TestFixture::type;
using Buffer = typename Config::BufferType;
Buffer buf{};
bitsery::BasicBufferWriter<Config> bw{buf};
bw.writeBits(3u, 2);
bw.align();
auto range1 = bw.getWrittenRange();
bw.flush();
auto range2 = bw.getWrittenRange();
EXPECT_THAT(std::distance(range1.begin(), range1.end()), Eq(1));
EXPECT_THAT(std::distance(range2.begin(), range2.end()), Eq(1));
}
//TEST(BufferWritingFixedBuffer, ) {
// FixedBufferConfig::BufferType buf{};
// bitsery::BasicBufferWriter<FixedBufferConfig> bw{buf};
// writeData(bw);
// bw.flush();
// auto r = bw.getWrittenRange();
// EXPECT_THAT(buf.begin(), r.begin());
//}

View File

@@ -161,7 +161,7 @@ TEST(DeltaSerializer, GeneralConceptTest) {
serialize(ser, yNew);
bw.flush();
bitsery::BufferReader br{ buf };
bitsery::BufferReader br{ bw.getWrittenRange() };
bitsery::DeltaDeserializer<bitsery::BufferReader, Y> des(br, y, yRead);
serialize(des, yRead);

View File

@@ -92,7 +92,8 @@ public:
};
size_t getBufferSize() const {
return buf.size();
auto range = bw->getWrittenRange();
return std::distance(range.begin(), range.end());
}
//since all containers .size() method returns size_t, it cannot be directly serialized, because size_t is platform dependant
@@ -107,7 +108,7 @@ public:
bitsery::Deserializer<bitsery::BufferReader> createDeserializer() {
bw->flush();
br = std::make_unique<bitsery::BufferReader>(buf);
br = std::make_unique<bitsery::BufferReader>(bw->getWrittenRange());
return {*br};
};
};