#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include TEST(Serialization, byteswap) { ASSERT_EQ(byteswap16(0x0102), 0x0201); ASSERT_EQ(byteswap32(0x01020304), 0x04030201u); ASSERT_EQ(byteswap64(0x0102030405060708ULL), 0x0807060504030201ULL); } static bool memoryEquals(const void* first, const void* second, size_t size) { const char* a = (const char*) first; const char* b = (const char*) second; size_t pos = 0; while (pos < size) { if(a[pos] == b[pos]) { pos++; continue; } std::cerr << "\nfound mismatch at position " << pos << ": \n"; for(int dumped = 0; dumped < 16 && pos + dumped < size; dumped++) std::cerr << int(unsigned(a[pos + dumped]) ) << " "; std::cerr << "\n"; for(int dumped = 0; dumped < 16 && pos + dumped < size; dumped++) std::cerr << int(unsigned(b[pos + dumped]) ) << " "; return false; } return true; } struct NoModifier { template const T& operator()(const T& t) { return t; } template T& operator()( T& t) { return t; } }; template> void testObject(Object& obj, const char* expBegin, const char* expEnd, Modifier mod = Modifier(), Eq eq = Eq()) { SCOPED_TRACE("testObject"); const size_t Size = expEnd - expBegin; { Serializer ser; ser % mod((const Object&) obj); ASSERT_EQ(ser.size(), Size); } // have valgrind catch buffer overflows std::vector outBuf(Size); { Serializer ser(&outBuf[0], Size); ser % mod((const Object&) obj); ASSERT_TRUE(ser.good()); ASSERT_EQ(ser.size(), Size); ASSERT_TRUE(memoryEquals(&outBuf[0], expBegin, Size)); } { Deserializer des(&outBuf[0], Size); Object output; des % mod(output); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), Size); ASSERT_TRUE(eq(obj, output)); } { Deserializer des(&outBuf[0], Size - 1); Object output; des % mod(output); ASSERT_FALSE(des.good()); } } template> void testObject(Object& obj, const char (&expected)[Size], Modifier mod = Modifier(), Eq eq = Eq()) { testObject(obj, expected, expected + Size, mod, eq); } template void testSimpleSerializer(T value, const char (&expected)[Size]) { testObject(value, expected, NoModifier(), std::equal_to()); } TEST(Serialization, isDirection) { { Serializer ser; ASSERT_TRUE(ser.isWriting()); ASSERT_FALSE(ser.isReading()); } { Deserializer des(0, 0); ASSERT_FALSE(des.isWriting()); ASSERT_TRUE(des.isReading()); } } TEST(Serialization, primitiveTypes) { { const char expectedFalse[] = { 0 }; const char expectedTrue[] = { 1 }; testSimpleSerializer(false, expectedFalse); testSimpleSerializer(true, expectedTrue); } { const char expected[] = { 'X' }; testSimpleSerializer('X', expected); } { const char expected[] = { '\xff' }; testSimpleSerializer(255, expected); } { const char expected[] = { 2, 1 }; testSimpleSerializer(258, expected); } { const char expected[] = { '\xff', '\xff' }; testSimpleSerializer(-1, expected); } { const char expected[] = { 0x01, 0x02, 0x03, 0x04 }; testSimpleSerializer(0x04030201, expected); } { const char expected[] = { '\xff', '\xff', '\xff', '\xff' }; testSimpleSerializer(-1, expected); } { const char expected[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; testSimpleSerializer(0x0807060504030201LL, expected); } { const char expected[] = { 1, 2, 3, 4, 5, 6, 7, '\xff' }; testSimpleSerializer(0xff07060504030201ULL, expected); } } TEST(Serialization, pair) { const char expected[] = { 1, 0, 0, 0, 1 }; testSimpleSerializer( std::pair(1, 1), expected); } TEST(Serialization, tuple) { const char expected[] = { 1, 0, 0, 0, 1, 's' }; testSimpleSerializer( std::tuple(1, 1, 's'), expected); } template static void testPadFieldTo(Ctx& ctx, uint8_t& a, uint16_t& b, uint32_t& c) { { PadFieldTo _(ctx, 3); ctx % a; } { PadFieldTo _(ctx, 2); ctx % b; } { PadFieldTo _(ctx, 8); } { PadFieldTo _(ctx, 2); ctx % c; } { PadFieldTo _(ctx, 8); ctx % c; } } TEST(Serialization, padFieldTo) { const char expected[] = { 1, 0, 0, 1, 1, // field empty 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, }; uint8_t a = 0x01; uint16_t b = 0x0101; uint32_t c = 0x01010101; { Serializer ser; ::testPadFieldTo(ser, a, b, c); ASSERT_EQ(ser.size(), sizeof(expected)); } { char outBuf[sizeof(expected)]; Serializer ser(outBuf, sizeof(outBuf)); ::testPadFieldTo(ser, a, b, c); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expected, sizeof(expected))); } { Deserializer des(expected, sizeof(expected)); uint8_t a = 0; uint16_t b = 0; uint32_t c = 0; ::testPadFieldTo(des, a, b, c); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(a, 0x01); ASSERT_EQ(b, 0x0101); ASSERT_EQ(c, 0x01010101u); } { Deserializer des(expected, sizeof(expected) - 1); uint8_t a = 0; uint16_t b = 0; uint32_t c = 0; ::testPadFieldTo(des, a, b, c); ASSERT_FALSE(des.good()); } } namespace { enum class TestEnum1 { first = 1, second = -2, }; GCC_COMPAT_ENUM_CLASS_OPEQNEQ(TestEnum1) struct EnumAsMod { auto operator()(TestEnum1& value) -> decltype(serdes::as(value)) { return serdes::as(value); } auto operator()(const TestEnum1& value) -> decltype(serdes::as(value)) { return serdes::as(value); } }; } template<> struct SerializeAs { typedef int16_t type; }; TEST(Serialization, serializeAs) { { const char expected[] = { 1, 0, '\xfe', '\xff', }; testSimpleSerializer(std::make_tuple(TestEnum1::first, TestEnum1::second), expected); } { const char expected[] = { 1, 0, 0, 0, 0, 0, 0, 0, }; TestEnum1 value = TestEnum1::first; testObject(value, expected, EnumAsMod()); } } TEST(Serialization, rawString) { { const char expected[] = { 2, 0, 0, 0, 'a', 'b', 0, }; const char* input = expected + 4; { Serializer ser; ser % serdes::rawString(input, 2); ASSERT_EQ(ser.size(), sizeof(expected)); } { char outBuf[sizeof(expected)]; Serializer ser(outBuf, sizeof(outBuf)); ser % serdes::rawString(input, 2); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expected, sizeof(expected))); } { Deserializer des(expected, sizeof(expected)); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(data, input); ASSERT_EQ(length, 2u); } // short length field { Deserializer des(expected, 1); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length); ASSERT_FALSE(des.good()); } // short data { Deserializer des(expected, sizeof(expected) - 1); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length); ASSERT_FALSE(des.good()); } } { const char expectedAligned[] = { 2, 0, 0, 0, 'a', 'b', 0, 0, }; const char* input = expectedAligned + 4; { Serializer ser; ser % serdes::rawString(input, 2, 4); ASSERT_EQ(ser.size(), sizeof(expectedAligned)); } { char outBuf[sizeof(expectedAligned)]; Serializer ser(outBuf, sizeof(outBuf)); ser % serdes::rawString(input, 2, 4); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expectedAligned, sizeof(expectedAligned))); } { Deserializer des(expectedAligned, sizeof(expectedAligned)); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length, 4); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expectedAligned)); ASSERT_EQ(data, input); ASSERT_EQ(length, 2u); } // short length field { Deserializer des(expectedAligned, 1); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length, 4); ASSERT_FALSE(des.good()); } // short data { Deserializer des(expectedAligned, sizeof(expectedAligned) - 1); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length, 4); ASSERT_FALSE(des.good()); } } { const char badTerminator[] = { 2, 0, 0, 0, 'a', 'b', 1, }; Deserializer des(badTerminator, sizeof(badTerminator)); const char* data = nullptr; unsigned length = 0; des % serdes::rawString(data, length); ASSERT_FALSE(des.good()); } } namespace { struct StringAlign4 { serdes::RawStringSer operator()(const std::string& s) { return serdes::stringAlign4(s); } serdes::StdString operator()( std::string& s) { return serdes::stringAlign4(s); } }; } TEST(Serialization, str) { { const char* input = "test"; const char expected[] = { 4, 0, 0, 0, 't', 'e', 's', 't', 0, }; testSimpleSerializer(std::string(input), expected); } { const char* input = "test"; const char expected[] = { 4, 0, 0, 0, 't', 'e', 's', 't', 0, 0, 0, 0, }; char outBuf[sizeof(expected)]; { Serializer ser(outBuf, sizeof(expected)); ser % serdes::rawString(input, strlen(input), 4); ASSERT_TRUE(ser.good()); ASSERT_EQ(ser.size(), sizeof(expected)); ASSERT_EQ(memcmp(outBuf, expected, sizeof(expected)), 0); } { Deserializer des(outBuf, sizeof(expected)); const char* output = nullptr; unsigned outLen = 0; des % serdes::rawString(output, outLen, 4); ASSERT_TRUE(des.good()); ASSERT_EQ(outLen, strlen(input)); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(strcmp(input, output), 0); } { Deserializer des(outBuf, sizeof(expected) - 1); const char* output = nullptr; unsigned outLen = 0; des % serdes::rawString(output, outLen, 4); ASSERT_FALSE(des.good()); } { std::string str = input; testObject(str, expected, StringAlign4()); } // one more, but with one byte of prefix. without the prefix, the result should still be the // same { const char expected[] = { 1, 4, 0, 0, 0, 't', 'e', 's', 't', 0, 0, 0, 0, }; char outBuf[sizeof(expected)]; { Serializer ser(outBuf, sizeof(expected)); ser % char(1) % serdes::stringAlign4(input); ASSERT_TRUE(ser.good()); ASSERT_EQ(ser.size(), sizeof(expected)); } ASSERT_EQ(memcmp(outBuf, expected, sizeof(expected) ), 0); { Deserializer des(outBuf, sizeof(expected)); const char* output = nullptr; unsigned outLen = 0; des.skip(1); des % serdes::rawString(output, outLen, 4); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(strcmp(input, output), 0); } { Deserializer des(outBuf, sizeof(expected) - 1); const char* output = nullptr; unsigned outLen = 0; des.skip(1); des % serdes::rawString(output, outLen, 4); ASSERT_FALSE(des.good()); } } } } namespace { struct HeapU32 { uint32_t value; template static void serialize(This obj, Ctx& ctx) { ctx % obj->value; } }; inline Deserializer& operator%(Deserializer& des, std::unique_ptr& ptr) { ptr.reset(new HeapU32); return des % *ptr; } } TEST(Serialization, backedPtr) { const char expected[] = { 1, 0, 0, 0 }; uint32_t value = 1; uint32_t* ptr = &value; { Serializer ser; ser % serdes::backedPtr(ptr, (const uint32_t&) value); ASSERT_EQ(ser.size(), sizeof(expected)); } { char outBuf[sizeof(expected)]; Serializer ser(outBuf, sizeof(outBuf)); ser % serdes::backedPtr(ptr, (const uint32_t&) value); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expected, sizeof(expected))); } { char outBuf[sizeof(expected)]; Serializer ser(outBuf, sizeof(outBuf)); ser % serdes::backedPtr((const uint32_t*) ptr, (const uint32_t&) value); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expected, sizeof(expected))); } { Deserializer des(expected, sizeof(expected)); uint32_t* ptr = nullptr; des % serdes::backedPtr(ptr, value); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(*ptr, 1u); ASSERT_EQ(ptr, &value); } { Deserializer des(expected, sizeof(expected)); const uint32_t* ptr = nullptr; des % serdes::backedPtr(ptr, value); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(*ptr, 1u); ASSERT_EQ(ptr, &value); } { Deserializer des(expected, sizeof(expected)); HeapU32 value{1}; HeapU32* ptr = &value; std::unique_ptr backing; des % serdes::backedPtr(ptr, backing); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(ptr->value, 1u); ASSERT_EQ(ptr, backing.get()); } { Deserializer des(expected, sizeof(expected) - 1); uint32_t* ptr = nullptr; des % serdes::backedPtr(ptr, value); ASSERT_FALSE(des.good()); } } TEST(Serialization, rawBlock) { const char expected[] = { 1, 0, 0, 0 }; { Serializer ser; ser % serdes::rawBlock(expected, 4); ASSERT_EQ(ser.size(), sizeof(expected)); } { char outBuf[sizeof(expected)]; Serializer ser(outBuf, sizeof(outBuf)); ser % serdes::rawBlock(expected, 4); ASSERT_EQ(ser.size(), sizeof(expected)); ASSERT_TRUE(ser.good()); ASSERT_TRUE(memoryEquals(outBuf, expected, sizeof(expected))); } { Deserializer des(expected, sizeof(expected)); const char* data; uint32_t size = 4; des % serdes::rawBlock(data, size); ASSERT_TRUE(des.good()); ASSERT_EQ(des.size(), sizeof(expected)); ASSERT_EQ(data, expected); } { Deserializer des(expected, sizeof(expected) - 1); const char* data; uint32_t size = 4; des % serdes::rawBlock(data, size); ASSERT_FALSE(des.good()); } } TEST(Serialization, block) { // wrapping size_t invalidates serializer { char buffer; Serializer ser(&buffer, 1); ser % char(1); ser.putBlock(nullptr, size_t(-1)); ASSERT_FALSE(ser.good()); } { char buffer; Deserializer des(&buffer, 1); des % buffer; des.getBlock(nullptr, size_t(-1)); ASSERT_FALSE(des.good()); } { char buffer; Deserializer des(&buffer, 1); des % buffer; des.skip(size_t(-1)); ASSERT_FALSE(des.good()); } // going past end invalidates serializer { char buffer; Serializer ser(&buffer, 1); ser % uint32_t(1); ASSERT_FALSE(ser.good()); } { char buffer; Deserializer des(&buffer, 1); des % buffer; des.skip(1); ASSERT_FALSE(des.good()); } // skipping nothing does nothing { char buffer; Serializer ser(&buffer, 1); ASSERT_EQ(ser.size(), 0u); ASSERT_TRUE(ser.good()); ser.skip(0); ASSERT_EQ(ser.size(), 0u); ASSERT_TRUE(ser.good()); } { char buffer; Deserializer des(&buffer, 1); des % buffer; ASSERT_EQ(des.size(), 1u); ASSERT_TRUE(des.good()); des.skip(0); ASSERT_EQ(des.size(), 1u); ASSERT_TRUE(des.good()); } // blocks zeroes memory { char buffer[128]; for (unsigned i = 0; i < sizeof(buffer); i++) buffer[i] = 1; Serializer ser(buffer, sizeof(buffer)); ser.skip(sizeof(buffer)); ASSERT_EQ(ser.size(), sizeof(buffer)); ASSERT_TRUE(ser.good()); for (unsigned i = 0; i < sizeof(buffer); i++) ASSERT_EQ(buffer[i], 0); } } namespace { struct NonPrimitive { uint32_t data; template static void serialize(This obj, Ctx& ctx) { ctx % obj->data; } friend bool operator==(NonPrimitive a, NonPrimitive b) { return a.data == b.data; } }; } TEST(Serialization, nonPrimitive) { NonPrimitive value{1}; const char expected[] = { 1, 0, 0, 0 }; testSimpleSerializer(value, expected); } namespace { template struct CollectionItem { uint8_t value; CollectionItem() = default; CollectionItem(uint8_t value) : value(value) {} template static void serialize(This obj, Ctx& ctx) { ctx % obj->value; } friend bool operator<(CollectionItem a, CollectionItem b) { return a.value < b.value; } friend bool operator==(CollectionItem a, CollectionItem b) { return a.value == b.value; } }; typedef CollectionItem ItemWithLength; typedef CollectionItem ItemWithoutLength; } template<> struct ListSerializationHasLength : std::true_type {}; template<> struct MapSerializationHasLength : std::true_type {}; template<> struct ListSerializationHasLength : std::false_type {}; template<> struct MapSerializationHasLength : std::false_type {}; template static void testCollection(const char (&content)[N], const typename CollectionWithLength::value_type& withLength, const typename CollectionWithoutLength::value_type& withoutLength) { SCOPED_TRACE(__PRETTY_FUNCTION__); // without length, empty { std::vector expected = {0, 0, 0, 0}; CollectionWithoutLength empty; testObject(empty, &expected.front(), &expected.back() + 1); } // with length, empty { std::vector expected = { 8, 0, 0, 0, 0, 0, 0, 0}; CollectionWithLength empty; testObject(empty, &expected.front(), &expected.back() + 1); } // without length, value { std::vector expected = {1, 0, 0, 0}; expected.insert(expected.end(), content, content + N); CollectionWithoutLength collection{{withoutLength}}; testObject(collection, &expected.front(), &expected.back() + 1); } // with length, value { // 64 to ensure that the result is well within the range of signed char static_assert(8 + N < 64); std::vector expected = { 8 + N, 0, 0, 0, 1, 0, 0, 0}; expected.insert(expected.end(), content, content + N); CollectionWithLength collection{{withLength}}; testObject(collection, &expected.front(), &expected.back() + 1); } // with incorrect length { std::vector expected = { 7, 0, 0, 0, 0, 0, 0, 0}; CollectionWithLength value; Deserializer des(&expected[0], expected.size()); des % value; ASSERT_FALSE(des.good()); } // with corrupted length { std::vector expected = {7, 0, 0}; CollectionWithLength value; Deserializer des(&expected[0], expected.size()); des % value; ASSERT_FALSE(des.good()); } // with length, corrupted size { std::vector expected = { 8, 0, 0, 0, 1, 0, 0}; CollectionWithLength value; Deserializer des(&expected[0], expected.size()); des % value; ASSERT_FALSE(des.good()); } // without length, corrupted size { std::vector expected = {1, 0, 0}; CollectionWithoutLength value; Deserializer des(&expected[0], expected.size()); des % value; ASSERT_FALSE(des.good()); } } TEST(Serialization, collections) { { const char expected[] = {1}; testCollection< std::list, std::list>(expected, 1, 1); testCollection< std::vector, std::vector>(expected, 1, 1); testCollection< std::set, std::set>(expected, 1, 1); } { const char expected[] = {1, 2}; testCollection< std::map, std::map>(expected, {1, 2}, {1, 2}); } } namespace { template struct AtomicMod { template serdes::AtomicAsSer operator()(const Atomic& t) const { return serdes::atomicAs(t); } template serdes::AtomicAsDes operator()(Atomic& t) const { return serdes::atomicAs(t); } }; struct AtomicEq { template bool operator()(const Atomic& a, const Atomic& b) const { return a.read() == b.read(); } }; } TEST(Serialization, atomic) { const char expected[] = {1, 0, 0, 0}; Atomic value(1); testObject(value, expected, AtomicMod(), AtomicEq()); } namespace { struct BaseType { uint8_t a; template static void serialize(This obj, Ctx& ctx) { ctx % obj->a; } }; struct DerivedType : BaseType { uint32_t b; template static void serialize(This obj, Ctx& ctx) { ctx % serdes::base(obj) % obj->b; } friend bool operator==(DerivedType a, DerivedType b) { return a.a == b.a && a.b == b.b; } }; } TEST(Serialization, base) { DerivedType value; value.a = 1; value.b = 2; const char expected[] = { 1, 2, 0, 0, 0 }; testSimpleSerializer(value, expected); } template class Collection, size_t N> static void testStringCollection(const char (&expected)[N]) { SCOPED_TRACE(__PRETTY_FUNCTION__); // empty { const char expected[] = { 8, 0, 0, 0, 0, 0, 0, 0}; Collection empty; testSimpleSerializer(empty, expected); } // values b, a { Collection values; values.insert(values.end(), "b"); values.insert(values.end(), "a"); testSimpleSerializer(values, expected); } // corrupt length field { const char input[] = { 0 }; Deserializer des(input, sizeof(input)); Collection value; des % value; ASSERT_FALSE(des.good()); } // corrupt size field { const char input[] = { 8, 0, 0, 0, 0, 0, 0}; Deserializer des(input, sizeof(input)); Collection value; des % value; ASSERT_FALSE(des.good()); } // too many items { const char input[] = { 8, 0, 0, 0, 1, 0, 0, 0}; Deserializer des(input, sizeof(input)); Collection value; des % value; ASSERT_FALSE(des.good()); } // too much length { const char input[] = { 9, 0, 0, 0, 0, 0, 0, 0}; Deserializer des(input, sizeof(input)); Collection value; des % value; ASSERT_FALSE(des.good()); } // dump not properly terminated { const char input[] = { 10, 0, 0, 0, 1, 0, 0, 0, 'a', 'a'}; Deserializer des(input, sizeof(input)); Collection value; des % value; ASSERT_FALSE(des.good()); } } TEST(Serialization, stringCollections) { { const char expected[] = { 12, 0, 0, 0, 2, 0, 0, 0, 'b', 0, 'a', 0}; testStringCollection(expected); testStringCollection(expected); } { const char expected[] = { 12, 0, 0, 0, 2, 0, 0, 0, 'a', 0, 'b', 0}; testStringCollection(expected); } }