range implementation complete

This commit is contained in:
fraillt
2017-02-17 15:51:34 +02:00
parent 5d2d547330
commit 08cd2b3dfa
9 changed files with 287 additions and 125 deletions

View File

@@ -51,8 +51,25 @@ struct BIGGER_TYPE<char> {
template <typename T>
constexpr size_t ARITHMETIC_OR_ENUM_SIZE = std::is_arithmetic<T>::value || std::is_enum<T>::value ? sizeof(T) : 0;
template<typename T, typename Enable = void>
struct SAME_SIZE_UNSIGNED_TYPE {
typedef std::make_unsigned_t<T> type;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_enum<T>::value>::type> {
typedef std::make_unsigned_t<std::underlying_type_t<T>> type;
};
template<typename T>
struct SAME_SIZE_UNSIGNED_TYPE<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
typedef std::conditional_t<std::is_same<T,float>::value, uint32_t, uint64_t> type;
};
template <typename T>
using UINT_FOR_FLOATING_POINT = std::conditional_t<std::is_same<T,float>::value, uint32_t, uint64_t>;
using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE<T>::type;
template <size_t SIZE>
struct ProcessAnyType {
@@ -84,78 +101,65 @@ constexpr size_t calcRequiredBits(T min, T max) {
}
template <typename T, class Enable = void>
class RangeSpec {
public:
template <typename T, typename Enable = void>
struct RangeSpec {
constexpr RangeSpec(T min, T max)
:_min{min},
_max{max},
_bitsRequired{calcRequiredBits(_min, _max)}
{
constexpr RangeSpec(T minValue, T maxValue)
:min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(min, max)}
{
}
constexpr size_t bitsRequired() const {
return _bitsRequired;
}
private:
const T _min;
const T _max;
const size_t _bitsRequired;
const T min;
const T max;
const size_t bitsRequired;
};
template <typename T>
class RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type> {
public:
constexpr RangeSpec(T min, T max):
_min{min},
_max{max},
_bitsRequired{calcRequiredBits(
static_cast<std::underlying_type_t<T>>(_min),
static_cast<std::underlying_type_t<T>>(_max))}
struct RangeSpec<T, typename std::enable_if<std::is_enum<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue):
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits(
static_cast<std::underlying_type_t<T>>(min),
static_cast<std::underlying_type_t<T>>(max))}
{
}
constexpr size_t bitsRequired() const {
return _bitsRequired;
}
const T min;
const T max;
const size_t bitsRequired;
};
private:
const T _min;
const T _max;
const size_t _bitsRequired;
//this class is used to make default RangeSpec float specialization always prefer constructor with precision
struct BitsConstraint {
explicit constexpr BitsConstraint(size_t bits):value{bits} {}
const size_t value;
};
template <typename T>
class RangeSpec<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
public:
//todo bits should be separate size not implicitly convertable from floating point types, so these constructors would be ambiguous
constexpr RangeSpec(T min, T max, size_t bits, int tmp):
_min{min},
_max{max},
_bitsRequired{bits}
struct RangeSpec<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
constexpr RangeSpec(T minValue, T maxValue, BitsConstraint bits):
min{minValue},
max{maxValue},
bitsRequired{bits.value}
{
}
constexpr RangeSpec(T min, T max, T precision):
_min{min},
_max{max},
_bitsRequired{calcRequiredBits<UINT_FOR_FLOATING_POINT<T>>({}, ((max - min) / precision))}
constexpr RangeSpec(T minValue, T maxValue, T precision):
min{minValue},
max{maxValue},
bitsRequired{calcRequiredBits<SAME_SIZE_UNSIGNED<T>>({}, ((max - min) / precision))}
{
}
constexpr size_t bitsRequired() const {
return _bitsRequired;
}
private:
const T _min;
const T _max;
const size_t _bitsRequired;
const T min;
const T max;
const size_t bitsRequired;
};

View File

@@ -28,7 +28,7 @@ public:
static_assert(std::numeric_limits<double>::is_iec559, "");
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_reader.template readBytes<ValueSize>(reinterpret_cast<UINT_FOR_FLOATING_POINT<T>&>(v));
_reader.template readBytes<ValueSize>(reinterpret_cast<SAME_SIZE_UNSIGNED<T>&>(v));
return *this;
}
@@ -53,8 +53,8 @@ public:
template <typename T>
Deserializer& range(T& v, const RangeSpec<T>& range) {
//ValueWriteProxy<T> proxy{v, range};
//_reader.template readBits(proxy.value(), r.bitsRequired());
_reader.template readBits(reinterpret_cast<SAME_SIZE_UNSIGNED<T>&>(v), range.bitsRequired);
setRangeValue(v, range);
return *this;
}
@@ -186,5 +186,28 @@ private:
};
/*
* functions for range
*/
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
v += r.min;
};
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
using VT = std::underlying_type_t<T>;
reinterpret_cast<VT&>(v) += static_cast<VT>(r.min);
};
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void setRangeValue(T& v, const RangeSpec<T>& r) {
using UIT = SAME_SIZE_UNSIGNED<T>;
const auto intRep = reinterpret_cast<UIT&>(v);
const UIT maxUint = (static_cast<UIT>(1) << r.bitsRequired) - 1;
v = r.min + (static_cast<T>(intRep) / maxUint) * (r.max - r.min);
};
#endif //TMP_DESERIALIZER_H

View File

@@ -28,7 +28,7 @@ public:
static_assert(std::numeric_limits<double>::is_iec559, "");
constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE;
_writter.template writeBytes<ValueSize>(reinterpret_cast<const UINT_FOR_FLOATING_POINT<T>&>(v));
_writter.template writeBytes<ValueSize>(reinterpret_cast<const SAME_SIZE_UNSIGNED<T>&>(v));
return *this;
}
@@ -51,9 +51,9 @@ public:
*/
template <typename T>
Serializer& range(const T& v, const RangeSpec<T>& r) {
//assert(r.isValid(v));
//_writter.template writeBits(r.value(v), r.bitsRequired());
Serializer& range(const T& v, const RangeSpec<T>& range) {
assert(isRangeValid(v, range));
_writter.template writeBits(getRangeValue(v,range), range.bitsRequired);
return *this;
}
@@ -174,7 +174,41 @@ private:
if (size)
_writter.template writeBuffer<VSIZE>(str, size);
}
};
/*
* functions for range
*/
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
bool isRangeValid(const T& v, const RangeSpec<T>& r) {
return !(r.min > v || v > r.max);
}
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
bool isRangeValid(const T& v, const RangeSpec<T>& r) {
using VT = std::underlying_type_t<T>;
return !(static_cast<VT>(r.min) > static_cast<VT>(v)
|| static_cast<VT>(v) > static_cast<VT>(r.max));
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
auto getRangeValue(const T& v, const RangeSpec<T>& r) {
return static_cast<SAME_SIZE_UNSIGNED<T>>(v - r.min);
};
template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
auto getRangeValue(const T& v, const RangeSpec<T>& r) {
return static_cast<SAME_SIZE_UNSIGNED<T>>(v) - static_cast<SAME_SIZE_UNSIGNED<T>>(r.min);
};
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
auto getRangeValue(const T& v, const RangeSpec<T>& r) {
using VT = SAME_SIZE_UNSIGNED<T>;
const VT maxUint = (static_cast<VT>(1) << r.bitsRequired) - 1;
const auto ratio = (v - r.min) / (r.max - r.min);
return static_cast<VT>(ratio * maxUint);
};

View File

@@ -15,29 +15,34 @@ include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
include_directories(${CMAKE_SOURCE_DIR}/include)
file(GLOB TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_executable(${TEST_PROJECT_NAME} ${TEST_SRC_FILES})
add_dependencies(${TEST_PROJECT_NAME} googletest)
set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY CXX_STANDARD 14)
set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
FOREACH(TEST_PROJECT_FILE ${TEST_SRC_FILES})
get_filename_component(TEST_PROJECT_NAME ${TEST_PROJECT_FILE} NAME_WE)
add_executable(${TEST_PROJECT_NAME} ${TEST_SRC_FILES})
add_dependencies(${TEST_PROJECT_NAME} googletest)
set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY CXX_STANDARD 14)
set_property(TARGET ${TEST_PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
if(NOT WIN32 OR MINGW)
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
target_link_libraries(${TEST_PROJECT_NAME} ${GTEST_LIBS_DIR}/lib${LIBNAME}.a )
ENDFOREACH()
else()
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
target_link_libraries(${TEST_PROJECT_NAME}
debug ${GTEST_LIBS_DIR}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
optimized ${GTEST_LIBS_DIR}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
ENDFOREACH()
endif()
if(NOT WIN32 OR MINGW)
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
target_link_libraries(${TEST_PROJECT_NAME} ${GTEST_LIBS_DIR}/lib${LIBNAME}.a )
ENDFOREACH()
else()
FOREACH(LIBNAME ${GTEST_LINK_LIBNAMES})
target_link_libraries(${TEST_PROJECT_NAME}
debug ${GTEST_LIBS_DIR}/DebugLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES}
optimized ${GTEST_LIBS_DIR}/ReleaseLibs/${CMAKE_FIND_LIBRARY_PREFIXES}${LIBNAME}${CMAKE_FIND_LIBRARY_SUFFIXES})
ENDFOREACH()
endif()
target_link_libraries(${TEST_PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME all_tests COMMAND $<TARGET_FILE:${TEST_PROJECT_NAME}>)
target_link_libraries(${TEST_PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME ${TEST_PROJECT_NAME} COMMAND $<TARGET_FILE:${TEST_PROJECT_NAME}>)
ENDFOREACH()

View File

@@ -6,17 +6,128 @@
#include "SerializationTestUtils.h"
using namespace testing;
TEST(Ranges, RequiredBitsIsConstexpr) {
TEST(SerializeRange, RequiredBitsIsConstexpr) {
constexpr RangeSpec<int> r1{0, 31};
static_assert(r1.bitsRequired() == 5);
static_assert(r1.bitsRequired == 5);
constexpr RangeSpec<MyEnumClass> r2{MyEnumClass::E1, MyEnumClass::E4};
static_assert(r2.bitsRequired() == 2);
static_assert(r2.bitsRequired == 2);
constexpr RangeSpec<double> r3{-1.0,1.0, 5u, 0};
static_assert(r3.bitsRequired() == 5);
constexpr RangeSpec<double> r3{-1.0,1.0, BitsConstraint{5u}};
//EXPECT_THAT(r1.bitsRequired, Eq(5));
static_assert(r3.bitsRequired == 5);
constexpr RangeSpec<float> r4{-1.0f,1.0f, 0.01f};
static_assert(r4.bitsRequired() == 8);
static_assert(r4.bitsRequired == 8);
}
}
TEST(SerializeRange, IntegerNegative) {
SerializationContext ctx;
constexpr RangeSpec<int> r1{-50, 50};
int t1{-8};
int res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(res1, Eq(t1));
}
TEST(SerializeRange, IntegerPositive) {
SerializationContext ctx;
constexpr RangeSpec<unsigned> r1{4, 10};
unsigned t1{8};
unsigned res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(res1, Eq(t1));
}
TEST(SerializeRange, EnumTypes) {
SerializationContext ctx;
constexpr RangeSpec<MyEnumClass> r1{MyEnumClass::E2, MyEnumClass::E4};
MyEnumClass t1{MyEnumClass::E2};
MyEnumClass res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(res1, Eq(t1));
}
TEST(SerializeRange, FloatUsingPrecisionConstraint1) {
SerializationContext ctx;
constexpr float precision{0.01f};
constexpr float min{-1.0f};
constexpr float max{1.0f};
float t1{0.5f};
constexpr RangeSpec<float> r1{min, max, precision};
float res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(res1, ::testing::FloatNear(t1, (max - min) * precision));
}
TEST(SerializeRange, DoubleUsingPrecisionConstraint2) {
SerializationContext ctx;
constexpr double precision{0.000002};
constexpr double min{50.0};
constexpr double max{100000.0};
double t1{38741.0};
constexpr RangeSpec<double> r1{min, max, precision};
double res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(5));
EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) * precision));
}
TEST(SerializeRange, FloatUsingBitsSizeConstraint1) {
SerializationContext ctx;
constexpr size_t bits = 8;
constexpr float min{-1.0f};
constexpr float max{1.0f};
float t1{0.5f};
constexpr RangeSpec<float> r1{min, max, BitsConstraint(bits)};
float res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(1));
EXPECT_THAT(res1, ::testing::FloatNear(t1, (max - min) / (static_cast<SAME_SIZE_UNSIGNED<float>>(1) << bits)));
}
TEST(SerializeRange, DoubleUsingBitsSizeConstraint2) {
SerializationContext ctx;
constexpr size_t bits = 50;
constexpr double min{50.0};
constexpr double max{100000.0};
double t1{38741};
constexpr RangeSpec<double> r1{min, max, BitsConstraint(bits)};
double res1;
ctx.createSerializer().range(t1, r1);
ctx.createDeserializer().range(res1, r1);
EXPECT_THAT(ctx.getBufferSize(), Eq(7));
EXPECT_THAT(res1, ::testing::DoubleNear(t1, (max - min) / (static_cast<SAME_SIZE_UNSIGNED<double>>(1) << bits)));
}

View File

@@ -74,6 +74,7 @@ public:
}
Deserializer<BufferReader> createDeserializer() {
bw->flush();
br = std::make_unique<BufferReader>(buf);
return {*br};
};

View File

@@ -6,7 +6,7 @@
#include "SerializationTestUtils.h"
using namespace testing;
TEST(SerializerText, BasicString) {
TEST(SerializeText, BasicString) {
SerializationContext ctx;
std::string t1 = "some random text";
std::string res;
@@ -19,7 +19,7 @@ TEST(SerializerText, BasicString) {
}
TEST(SerializerText, WhenSizeOfTypeNotEqualsOneThenSetSizeExplicitly) {
TEST(SerializeText, WhenSizeOfTypeNotEqualsOneThenSetSizeExplicitly) {
SerializationContext ctx;
constexpr auto VSIZE = sizeof(char32_t);
std::basic_string<char32_t> t1 = U"some random text";
@@ -32,7 +32,7 @@ TEST(SerializerText, WhenSizeOfTypeNotEqualsOneThenSetSizeExplicitly) {
EXPECT_THAT(res, ContainerEq(t1));
}
TEST(SerializerText, BasicStringUseSizeMethodNotNullterminatedLength) {
TEST(SerializeText, BasicStringUseSizeMethodNotNullterminatedLength) {
SerializationContext ctx;
std::wstring t1(L"some random text\0xxxxxx", 20);
std::wstring wres;
@@ -64,7 +64,7 @@ TEST(SerializerText, BasicStringUseSizeMethodNotNullterminatedLength) {
const int CARR_LENGTH = 10;
TEST(SerializerText, CArraySerializesTextLength) {
TEST(SerializeText, CArraySerializesTextLength) {
SerializationContext ctx;
char t1[CARR_LENGTH]{"some text"};
char r1[CARR_LENGTH]{};
@@ -89,7 +89,7 @@ TEST(SerializerText, CArraySerializesTextLength) {
EXPECT_THAT(r1, ContainerEq(t1));
}
TEST(SerializerText, WhenCArrayWithLargerTypeThenSetSizeExplicitly) {
TEST(SerializeText, WhenCArrayWithLargerTypeThenSetSizeExplicitly) {
SerializationContext ctx;
char32_t t1[10]{U"some text"};
char32_t r1[10]{};
@@ -103,7 +103,7 @@ TEST(SerializerText, WhenCArrayWithLargerTypeThenSetSizeExplicitly) {
}
TEST(SerializerText, WhenCArrayNotNullterminatedThenMakeItNullterminated) {
TEST(SerializeText, WhenCArrayNotNullterminatedThenMakeItNullterminated) {
SerializationContext ctx;
char t1[CARR_LENGTH]{"some text"};
//make last character not nullterminated

View File

@@ -3,16 +3,12 @@
//
#include <gmock/gmock.h>
#include <Deserializer.h>
#include <BufferReader.h>
#include "BufferWriter.h"
#include "Serializer.h"
#include "SerializationTestUtils.h"
#include "DeltaSerializer.h"
#include "DeltaDeserializer.h"
#include <list>
#include <array>
using testing::Eq;
using testing::StrEq;
@@ -64,11 +60,9 @@ SERIALIZE(Y)
}
TEST(Serializer, GeneralConceptTest) {
TEST(SerializeObject, GeneralConceptTest) {
//std::string buf;
std::vector<uint8_t> buf{};
BufferWriter bw{ buf };
Serializer<BufferWriter> ser(bw);
SerializationContext ctx;
Y y{};
y.y = 3423;
y.arr[0] = 111;
@@ -87,16 +81,17 @@ TEST(Serializer, GeneralConceptTest) {
z.x = X{ 234 };
serialize(ser, y);
serialize(ser, z);
auto ser = ctx.createSerializer();
ser.object(y);
ser.object(z);
BufferReader br{ buf };
Deserializer<BufferReader> des(br);
Y yres{};
Z zres{};
serialize(des, yres);
serialize(des, zres);
auto des = ctx.createDeserializer();
des.object(yres);
des.object(zres);
EXPECT_THAT(yres.y, Eq(y.y));
EXPECT_THAT(yres.vx, ContainerEq(y.vx));

View File

@@ -3,36 +3,25 @@
//
#include <gmock/gmock.h>
#include <Deserializer.h>
#include <BufferReader.h>
#include "BufferWriter.h"
#include "Serializer.h"
#include "SerializationTestUtils.h"
using testing::Eq;
template <typename T>
bool SerializeDeserializeValue(const T& v) {
T res{};
std::vector<uint8_t> buf{};
BufferWriter bw{buf};
Serializer<BufferWriter> ser(bw);
ser.value(v);
bw.flush();
BufferReader br{buf};
Deserializer<BufferReader> des(br);
des.value(res);
SerializationContext ctx;
ctx.createSerializer().value(v);
ctx.createDeserializer().value(res);
return v == res;
}
TEST(SerializerValues, IntegerTypes) {
TEST(SerializeValues, IntegerTypes) {
EXPECT_THAT(SerializeDeserializeValue(-449874), Eq(true));
EXPECT_THAT(SerializeDeserializeValue(34u), Eq(true));
}
TEST(SerializerValues, EnumTypes) {
TEST(SerializeValues, EnumTypes) {
enum E1{
A1,B1,C1,D1
};
@@ -47,12 +36,12 @@ TEST(SerializerValues, EnumTypes) {
EXPECT_THAT(SerializeDeserializeValue(E3::C3), Eq(true));
}
TEST(SerializerValues, FloatingPointTypes) {
TEST(SerializeValues, FloatingPointTypes) {
EXPECT_THAT(SerializeDeserializeValue(-484.465), Eq(true));
EXPECT_THAT(SerializeDeserializeValue(0.00000015f), Eq(true));
}
TEST(SerializerValues, ExplicitTypeSize) {
TEST(SerializeValues, ExplicitTypeSize) {
int v{23472};
constexpr size_t TSIZE = sizeof(v);
std::vector<uint8_t> buf{};