diff --git a/include/Common.h b/include/Common.h index 1c6a6f0..0f1c2aa 100644 --- a/include/Common.h +++ b/include/Common.h @@ -51,8 +51,25 @@ struct BIGGER_TYPE { template constexpr size_t ARITHMETIC_OR_ENUM_SIZE = std::is_arithmetic::value || std::is_enum::value ? sizeof(T) : 0; + +template +struct SAME_SIZE_UNSIGNED_TYPE { + typedef std::make_unsigned_t type; +}; + +template +struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { + typedef std::make_unsigned_t> type; +}; + +template +struct SAME_SIZE_UNSIGNED_TYPE::value>::type> { + typedef std::conditional_t::value, uint32_t, uint64_t> type; +}; + template -using UINT_FOR_FLOATING_POINT = std::conditional_t::value, uint32_t, uint64_t>; +using SAME_SIZE_UNSIGNED = typename SAME_SIZE_UNSIGNED_TYPE::type; + template struct ProcessAnyType { @@ -84,78 +101,65 @@ constexpr size_t calcRequiredBits(T min, T max) { } -template -class RangeSpec { -public: +template +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 -class RangeSpec::value>::type> { -public: - - constexpr RangeSpec(T min, T max): - _min{min}, - _max{max}, - _bitsRequired{calcRequiredBits( - static_cast>(_min), - static_cast>(_max))} +struct RangeSpec::value>::type> { + + constexpr RangeSpec(T minValue, T maxValue): + min{minValue}, + max{maxValue}, + bitsRequired{calcRequiredBits( + static_cast>(min), + static_cast>(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 -class RangeSpec::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::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>({}, ((max - min) / precision))} + constexpr RangeSpec(T minValue, T maxValue, T precision): + min{minValue}, + max{maxValue}, + bitsRequired{calcRequiredBits>({}, ((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; }; diff --git a/include/Deserializer.h b/include/Deserializer.h index 4ff3b85..c2aecac 100644 --- a/include/Deserializer.h +++ b/include/Deserializer.h @@ -28,7 +28,7 @@ public: static_assert(std::numeric_limits::is_iec559, ""); constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE; - _reader.template readBytes(reinterpret_cast&>(v)); + _reader.template readBytes(reinterpret_cast&>(v)); return *this; } @@ -53,8 +53,8 @@ public: template Deserializer& range(T& v, const RangeSpec& range) { - //ValueWriteProxy proxy{v, range}; - //_reader.template readBits(proxy.value(), r.bitsRequired()); + _reader.template readBits(reinterpret_cast&>(v), range.bitsRequired); + setRangeValue(v, range); return *this; } @@ -186,5 +186,28 @@ private: }; +/* + * functions for range + */ +template::value>::type* = nullptr> +void setRangeValue(T& v, const RangeSpec& r) { + v += r.min; +}; + +template::value>::type* = nullptr> +void setRangeValue(T& v, const RangeSpec& r) { + using VT = std::underlying_type_t; + reinterpret_cast(v) += static_cast(r.min); +}; + +template::value>::type* = nullptr> +void setRangeValue(T& v, const RangeSpec& r) { + using UIT = SAME_SIZE_UNSIGNED; + const auto intRep = reinterpret_cast(v); + const UIT maxUint = (static_cast(1) << r.bitsRequired) - 1; + v = r.min + (static_cast(intRep) / maxUint) * (r.max - r.min); +}; + + #endif //TMP_DESERIALIZER_H diff --git a/include/Serializer.h b/include/Serializer.h index e216a6a..3090bc1 100644 --- a/include/Serializer.h +++ b/include/Serializer.h @@ -28,7 +28,7 @@ public: static_assert(std::numeric_limits::is_iec559, ""); constexpr size_t ValueSize = VSIZE == 0 ? sizeof(T) : VSIZE; - _writter.template writeBytes(reinterpret_cast&>(v)); + _writter.template writeBytes(reinterpret_cast&>(v)); return *this; } @@ -51,9 +51,9 @@ public: */ template - Serializer& range(const T& v, const RangeSpec& r) { - //assert(r.isValid(v)); - //_writter.template writeBits(r.value(v), r.bitsRequired()); + Serializer& range(const T& v, const RangeSpec& 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(str, size); } +}; +/* + * functions for range + */ + +template::value>::type* = nullptr> +bool isRangeValid(const T& v, const RangeSpec& r) { + return !(r.min > v || v > r.max); +} + +template::value>::type* = nullptr> +bool isRangeValid(const T& v, const RangeSpec& r) { + using VT = std::underlying_type_t; + return !(static_cast(r.min) > static_cast(v) + || static_cast(v) > static_cast(r.max)); +} + + +template::value>::type* = nullptr> +auto getRangeValue(const T& v, const RangeSpec& r) { + return static_cast>(v - r.min); +}; + +template::value>::type* = nullptr> +auto getRangeValue(const T& v, const RangeSpec& r) { + return static_cast>(v) - static_cast>(r.min); +}; + +template::value>::type* = nullptr> +auto getRangeValue(const T& v, const RangeSpec& r) { + using VT = SAME_SIZE_UNSIGNED; + const VT maxUint = (static_cast(1) << r.bitsRequired) - 1; + const auto ratio = (v - r.min) / (r.max - r.min); + return static_cast(ratio * maxUint); }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cff0661..3c03afb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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_link_libraries(${TEST_PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) + add_test(NAME ${TEST_PROJECT_NAME} COMMAND $) + +ENDFOREACH() diff --git a/tests/SerializationRangeTests.cpp b/tests/SerializationRangeTests.cpp index 418bb4b..c589193 100644 --- a/tests/SerializationRangeTests.cpp +++ b/tests/SerializationRangeTests.cpp @@ -6,17 +6,128 @@ #include "SerializationTestUtils.h" using namespace testing; -TEST(Ranges, RequiredBitsIsConstexpr) { +TEST(SerializeRange, RequiredBitsIsConstexpr) { constexpr RangeSpec r1{0, 31}; - static_assert(r1.bitsRequired() == 5); + static_assert(r1.bitsRequired == 5); constexpr RangeSpec r2{MyEnumClass::E1, MyEnumClass::E4}; - static_assert(r2.bitsRequired() == 2); + static_assert(r2.bitsRequired == 2); - constexpr RangeSpec r3{-1.0,1.0, 5u, 0}; - static_assert(r3.bitsRequired() == 5); + constexpr RangeSpec r3{-1.0,1.0, BitsConstraint{5u}}; + //EXPECT_THAT(r1.bitsRequired, Eq(5)); + static_assert(r3.bitsRequired == 5); constexpr RangeSpec r4{-1.0f,1.0f, 0.01f}; - static_assert(r4.bitsRequired() == 8); + static_assert(r4.bitsRequired == 8); -} \ No newline at end of file +} + +TEST(SerializeRange, IntegerNegative) { + SerializationContext ctx; + constexpr RangeSpec 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 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 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 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 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 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>(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 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>(1) << bits))); +} diff --git a/tests/SerializationTestUtils.h b/tests/SerializationTestUtils.h index 08e0f1e..b44c913 100644 --- a/tests/SerializationTestUtils.h +++ b/tests/SerializationTestUtils.h @@ -74,6 +74,7 @@ public: } Deserializer createDeserializer() { + bw->flush(); br = std::make_unique(buf); return {*br}; }; diff --git a/tests/SerializationTextTests.cpp b/tests/SerializationTextTests.cpp index 6f04694..b991181 100644 --- a/tests/SerializationTextTests.cpp +++ b/tests/SerializationTextTests.cpp @@ -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 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 diff --git a/tests/SerializerObjectsTests.cpp b/tests/SerializerObjectsTests.cpp index 3e0327f..80bbcf9 100644 --- a/tests/SerializerObjectsTests.cpp +++ b/tests/SerializerObjectsTests.cpp @@ -3,16 +3,12 @@ // #include -#include -#include -#include "BufferWriter.h" -#include "Serializer.h" +#include "SerializationTestUtils.h" #include "DeltaSerializer.h" #include "DeltaDeserializer.h" #include -#include using testing::Eq; using testing::StrEq; @@ -64,11 +60,9 @@ SERIALIZE(Y) } -TEST(Serializer, GeneralConceptTest) { +TEST(SerializeObject, GeneralConceptTest) { //std::string buf; - std::vector buf{}; - BufferWriter bw{ buf }; - Serializer 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 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)); diff --git a/tests/SerializerValuesTests.cpp b/tests/SerializerValuesTests.cpp index 45d7d3e..4153ef3 100644 --- a/tests/SerializerValuesTests.cpp +++ b/tests/SerializerValuesTests.cpp @@ -3,36 +3,25 @@ // #include -#include -#include -#include "BufferWriter.h" -#include "Serializer.h" - +#include "SerializationTestUtils.h" using testing::Eq; template bool SerializeDeserializeValue(const T& v) { T res{}; - std::vector buf{}; - - BufferWriter bw{buf}; - Serializer ser(bw); - ser.value(v); - bw.flush(); - - BufferReader br{buf}; - Deserializer 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 buf{};