polymorphism improvements and new CompactValue extension

This commit is contained in:
Mindaugas
2019-01-08 15:06:29 +02:00
parent 2e62bd08e3
commit a2ecf8d7b0
13 changed files with 520 additions and 55 deletions

View File

@@ -44,7 +44,7 @@ foreach (TestFile ${TestSourceFiles})
add_executable(${TestName} ${TestFile})
target_link_libraries(${TestName} PRIVATE GTest::Main Bitsery::bitsery)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++)
target_compile_options(${TestName} PRIVATE -Wextra -Wno-missing-braces -Wpedantic -Weffc++ -Wno-c++14-extensions)
endif()
add_test(NAME ${TestName} COMMAND $<TARGET_FILE:${TestName}>)

View File

@@ -0,0 +1,241 @@
//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 <bitsery/ext/compact_value.h>
#include <gmock/gmock.h>
#include "serialization_test_utils.h"
#include <bitsery/traits/array.h>
#include <iostream>
#include <bitset>
#include <chrono>
using testing::Eq;
using bitsery::ext::CompactValue;
using bitsery::ext::CompactValueAsObject;
using bitsery::EndiannessType;
// helper function, that gets value filled with specified number of bits
template <typename TValue>
TValue getValue(bool isPositive, size_t significantBits) {
TValue v = isPositive ? 0 : -1;
if (significantBits == 0)
return v;
using TUnsigned = typename std::make_unsigned<TValue>::type;
TUnsigned mask = {};
mask = ~mask; // invert shiftByBits
auto shiftBy = bitsery::details::BitsSize<TValue>::value - significantBits;
mask >>= shiftBy;
//cast to unsigned when applying mask
return (TUnsigned)v ^ mask;
}
// helper function, that serialize and return deserialized value
template <typename TSerContext, typename TValue>
std::pair<TValue, size_t> serializeAndGetDeserialized(TValue data) {
TSerContext ctx;
TValue res{};
ctx.createSerializer().template ext<sizeof(TValue)>(data, CompactValue{});
ctx.createDeserializer().template ext<sizeof(TValue)>(res, CompactValue{});
return {res, ctx.getBufferSize()};
}
struct LittleEndianConfig: public bitsery::DefaultConfig {
static constexpr EndiannessType NetworkEndianness = EndiannessType::LittleEndian;
};
struct BigEndianConfig: public bitsery::DefaultConfig {
static constexpr EndiannessType NetworkEndianness = EndiannessType::BigEndian;
};
template <typename TValue, bool isPositiveNr, typename TConfig>
struct TC {
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
using Value = TValue;
using Config = TConfig;
bool isPositive = isPositiveNr;
};
template<typename T>
class SerializeExtensionCompactValueCorrectness : public testing::Test {
public:
using TestCase = T;
};
using AllValueSizesTestCases = ::testing::Types<
TC<uint8_t, true, LittleEndianConfig>,
TC<uint16_t, true, LittleEndianConfig>,
TC<uint32_t, true, LittleEndianConfig>,
TC<uint64_t, true, LittleEndianConfig>,
TC<int8_t, true, LittleEndianConfig>,
TC<int16_t, true, LittleEndianConfig>,
TC<int32_t, true, LittleEndianConfig>,
TC<int64_t, true, LittleEndianConfig>,
TC<int8_t, false, LittleEndianConfig>,
TC<int16_t, false, LittleEndianConfig>,
TC<int32_t, false, LittleEndianConfig>,
TC<int64_t, false, LittleEndianConfig>,
TC<uint8_t, true, BigEndianConfig>,
TC<uint16_t, true, BigEndianConfig>,
TC<uint32_t, true, BigEndianConfig>,
TC<uint64_t, true, BigEndianConfig>,
TC<int8_t, true, BigEndianConfig>,
TC<int16_t, true, BigEndianConfig>,
TC<int32_t, true, BigEndianConfig>,
TC<int64_t, true, BigEndianConfig>,
TC<int8_t, false, BigEndianConfig>,
TC<int16_t, false, BigEndianConfig>,
TC<int32_t, false, BigEndianConfig>,
TC<int64_t, false, BigEndianConfig>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueCorrectness, AllValueSizesTestCases);
TYPED_TEST(SerializeExtensionCompactValueCorrectness, TestDifferentSizeValues) {
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
for (auto i = 0u; i < bitsery::details::BitsSize<TValue>::value + 1; ++i) {
auto data = getValue<TValue>(tc.isPositive, i);
auto res = serializeAndGetDeserialized<BasicSerializationContext<typename TCase::Config, void>>(data);
EXPECT_THAT(res.first, Eq(data));
}
}
// this stucture will contain test data and result, as type paramters
template <typename TValue, bool isPositiveNr, size_t significantBits, size_t resultBytes>
struct SizeTC {
static_assert(isPositiveNr || std::is_signed<TValue>::value, "");
static_assert(bitsery::details::BitsSize<TValue>::value >= significantBits, "");
using Value = TValue;
bool isPositive = isPositiveNr;
size_t fillBits = significantBits;
size_t bytesCount = resultBytes;
};
template<typename T>
class SerializeExtensionCompactValueRequiredBytes : public testing::Test {
public:
using TestCase = T;
};
using RequiredBytesTestCases = ::testing::Types<
//1 byte always writes to 1 byte
SizeTC<uint8_t, true, 0,1>,
SizeTC<uint8_t, true, 8,1>,
SizeTC<int8_t, false, 0,1>,
SizeTC<int8_t, true, 8,1>,
//2 byte, +1 byte after 15 significant bits
SizeTC<uint16_t, true, 7,1>,
SizeTC<uint16_t, true, 8,2>,
SizeTC<uint16_t, true, 14,2>,
SizeTC<uint16_t, true, 15,3>,
//2 byte, +1 byte after 15-1 significant bits (1 bit for sign)
SizeTC<int16_t, true, 6,1>,
SizeTC<int16_t, false, 7,2>,
SizeTC<int16_t, true, 13,2>,
SizeTC<int16_t, false, 14,3>,
//4 byte, +1 byte after 29 significant bits
SizeTC<uint32_t, true, 14,2>,
SizeTC<uint32_t, true, 21,3>,
SizeTC<uint32_t, true, 28,4>,
SizeTC<uint32_t, true, 29,5>,
SizeTC<uint32_t, true, 32,5>,
//4 byte
SizeTC<int32_t, true, 13,2>,
SizeTC<int32_t, false, 20,3>,
SizeTC<int32_t, true, 27,4>,
SizeTC<int32_t, false, 28,5>,
SizeTC<int32_t, true, 31,5>,
//8 byte, +1 byte after 57 significant bits, or +2 byte when all bits are significant
SizeTC<uint64_t, true, 28,4>,
SizeTC<uint64_t, true, 35,5>,
SizeTC<uint64_t, true, 42,6>,
SizeTC<uint64_t, true, 49,7>,
SizeTC<uint64_t, true, 56,8>,
SizeTC<uint64_t, true, 57,9>,
SizeTC<uint64_t, true, 63,9>,
SizeTC<uint64_t, true, 64,10>,
//8 byte,
SizeTC<int64_t, true, 27,4>,
SizeTC<int64_t, false, 34,5>,
SizeTC<int64_t, true, 41,6>,
SizeTC<int64_t, false, 48,7>,
SizeTC<int64_t, true, 55,8>,
SizeTC<int64_t, false, 56,9>,
SizeTC<int64_t, true, 62,9>,
SizeTC<int64_t, false, 63,10>
>;
TYPED_TEST_CASE(SerializeExtensionCompactValueRequiredBytes, RequiredBytesTestCases);
TYPED_TEST(SerializeExtensionCompactValueRequiredBytes, Test) {
using TCase = typename TestFixture::TestCase;
using TValue = typename TCase::Value;
TCase tc{};
TValue data = getValue<TValue>(tc.isPositive, tc.fillBits);
auto res = serializeAndGetDeserialized<SerializationContext>(data);
EXPECT_THAT(res.first, Eq(data));
EXPECT_THAT(res.second, tc.bytesCount);
}
enum b1En: uint8_t {
A,B,C,D=54,E
};
enum class b8En: int64_t {
A=-874987489,B,C=0,D,E=489748978, F,G
};
TEST(SerializeExtensionCompactValueEnum, TestEnums) {
auto d1 = b1En::E;
auto d2 = b8En::B;
auto d3 = b8En::F;
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d1).first, Eq(d1));
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d2).first, Eq(d2));
EXPECT_THAT(serializeAndGetDeserialized<SerializationContext>(d3).first, Eq(d3));
}
TEST(SerializeExtensionCompactValueAsObjectDeserializeOverflow, TestEnums) {
SerializationContext ctx;
auto data = getValue<uint32_t >(true, 17);
uint16_t res{};
auto& ser = ctx.createSerializer();
ser.ext(data, CompactValueAsObject{});
auto& des = ctx.createDeserializer();
des.ext(res, CompactValueAsObject{});
auto& rd = bitsery::AdapterAccess::getReader(des);
EXPECT_THAT(data, ::testing::Ne(res));
EXPECT_THAT(rd.error(), Eq(bitsery::ReaderError::DataOverflow));
}

View File

@@ -152,7 +152,7 @@ public:
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -160,7 +160,7 @@ public:
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -322,7 +322,7 @@ TEST_F(SerializeExtensionPointerPolymorphicTypes,
auto &des = sctx.createDeserializer(&plctx);
auto &pc = std::get<2>(plctx);
pc.clear();
pc.registerBasesList(des, bitsery::ext::PolymorphicClassesList<BaseClone>{});
pc.registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<BaseClone>{});
des.ext(baseRes, PointerOwner{});
EXPECT_THAT(sctx.br->error(), Eq(bitsery::ReaderError::InvalidPointer));
}

View File

@@ -160,7 +160,7 @@ public:
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).template registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -168,7 +168,7 @@ public:
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).template registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -407,7 +407,7 @@ public:
auto &res = sctx.createSerializer(&plctx);
std::get<2>(plctx).clear();
//bind serializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TSerializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}
@@ -415,7 +415,7 @@ public:
auto &res = sctx.createDeserializer(&plctx);
std::get<2>(plctx).clear();
//bind deserializer with classes
std::get<2>(plctx).registerBasesList(res, bitsery::ext::PolymorphicClassesList<Base>{});
std::get<2>(plctx).registerBasesList<SerContext::TDeserializer>(bitsery::ext::PolymorphicClassesList<Base>{});
return res;
}