From dc3111c77deba1b3e82b7ecc858f6234d2bc1737 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Fri, 21 Feb 2014 00:29:15 +0400 Subject: [PATCH] ScalarCodec implementation; more tests to come --- .../internal/marshalling/scalar_codec.hpp | 143 ++++++++++++++++++ libuavcan/include/uavcan/internal/util.hpp | 7 + libuavcan/test/marshalling/scalar_codec.cpp | 81 ++++++++++ 3 files changed, 231 insertions(+) create mode 100644 libuavcan/include/uavcan/internal/marshalling/scalar_codec.hpp create mode 100644 libuavcan/test/marshalling/scalar_codec.cpp diff --git a/libuavcan/include/uavcan/internal/marshalling/scalar_codec.hpp b/libuavcan/include/uavcan/internal/marshalling/scalar_codec.hpp new file mode 100644 index 0000000000..3049614562 --- /dev/null +++ b/libuavcan/include/uavcan/internal/marshalling/scalar_codec.hpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uavcan +{ + +class ScalarCodec +{ + BitStream& stream_; + + template + static void swapByteOrder(uint8_t (&bytes)[SIZE]) + { + for (int i = 0, j = SIZE - 1; i < j; i++, j--) + { + const uint8_t c = bytes[i]; + bytes[i] = bytes[j]; + bytes[j] = c; + } + } + + template + static typename EnableIf<(BITLEN > 8)>::Type + convertByteOrder(uint8_t (&bytes)[SIZE]) + { +#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) + static const bool big_endian = BYTE_ORDER == BIG_ENDIAN; +#else + union { long int l; char c[sizeof(long int)]; } u; + u.l = 1; + const bool big_endian = u.c[sizeof(long int) - 1] == 1; +#endif + if (big_endian) + swapByteOrder(bytes); + } + + template + static typename EnableIf<(BITLEN <= 8)>::Type + convertByteOrder(uint8_t (&bytes)[SIZE]) + { + (void)bytes; + } + + template + static typename EnableIf::is_signed && ((sizeof(T) * 8) > BITLEN)>::Type + fixTwosComplement(T& value) + { + StaticAssert::is_integer>::check(); // Not applicable to floating point types + + if (value & (T(1) << (BITLEN - 1))) // The most significant bit is set --> negative + value |= 0xFFFFFFFFFFFFFFFF & ~((T(1) << BITLEN) - 1); + } + + template + static typename EnableIf::is_signed || ((sizeof(T) * 8) == BITLEN)>::Type + fixTwosComplement(T& value) + { + (void)value; + } + + template + static typename EnableIf<((sizeof(T) * 8) > BITLEN)>::Type + clearExtraBits(T& value) + { + value &= (1 << BITLEN) - 1; // Signedness doesn't matter + } + + template + static typename EnableIf<((sizeof(T) * 8) == BITLEN)>::Type + clearExtraBits(T& value) + { + (void)value; + } + + template + void validate() + { + StaticAssert<((sizeof(T) * 8) >= BITLEN)>::check(); + StaticAssert<(BITLEN <= BitStream::MAX_BITS_PER_RW)>::check(); + StaticAssert::is_signed ? (BITLEN > 1) : 1>::check(); + } + +public: + ScalarCodec(BitStream& stream) + : stream_(stream) + { } + + template + int encode(const T value) + { + validate(); + union ByteUnion + { + T value; + uint8_t bytes[sizeof(T)]; + } byte_union; + byte_union.value = value; + + clearExtraBits(byte_union.value); + convertByteOrder(byte_union.bytes); + + // Underlying stream class assumes that more significant bits have lower index, so we need to shift some. + byte_union.bytes[BITLEN / 8] <<= (8 - (BITLEN % 8)) & 7; + + return stream_.write(byte_union.bytes, BITLEN); + } + + template + int decode(T& value) + { + validate(); + union ByteUnion + { + T value; + uint8_t bytes[sizeof(T)]; + } byte_union; + std::fill(byte_union.bytes, byte_union.bytes + sizeof(T), 0); + + const int read_res = stream_.read(byte_union.bytes, BITLEN); + if (read_res <= 0) + return read_res; + + byte_union.bytes[BITLEN / 8] >>= (8 - (BITLEN % 8)) & 7; // As in encode(), vice versa + + convertByteOrder(byte_union.bytes); + fixTwosComplement(byte_union.value); + + value = byte_union.value; + return read_res; + } +}; + +} diff --git a/libuavcan/include/uavcan/internal/util.hpp b/libuavcan/include/uavcan/internal/util.hpp index 7f33bd50ea..53f548f978 100644 --- a/libuavcan/include/uavcan/internal/util.hpp +++ b/libuavcan/include/uavcan/internal/util.hpp @@ -36,4 +36,11 @@ protected: Noncopyable() { } }; + +template +struct EnableIf { }; + +template +struct EnableIf { typedef T Type; }; + } diff --git a/libuavcan/test/marshalling/scalar_codec.cpp b/libuavcan/test/marshalling/scalar_codec.cpp new file mode 100644 index 0000000000..40da7ac4d0 --- /dev/null +++ b/libuavcan/test/marshalling/scalar_codec.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#include +#include + + +TEST(ScalarCodec, Basic) +{ + uavcan::StaticTransferBuffer<38> buf; + uavcan::BitStream bs_wr(buf); + uavcan::ScalarCodec sc_wr(bs_wr); + + { + uint64_t u64 = 0; + ASSERT_EQ(0, sc_wr.decode<64>(u64)); // Out of buffer space + } + + /* + * Encoding some variables + */ + ASSERT_EQ(1, sc_wr.encode<12>((uint16_t)0xbeda)); // --> 0xeda + ASSERT_EQ(1, sc_wr.encode<1>((uint8_t)1)); + ASSERT_EQ(1, sc_wr.encode<1>((uint16_t)0)); + ASSERT_EQ(1, sc_wr.encode<4>((uint8_t)8)); + ASSERT_EQ(1, sc_wr.encode<32>((uint32_t)0xdeadbeef)); + ASSERT_EQ(1, sc_wr.encode<3>((int8_t)-1)); + ASSERT_EQ(1, sc_wr.encode<4>((int8_t)-6)); + ASSERT_EQ(1, sc_wr.encode<20>((int32_t)-123456)); + ASSERT_EQ(1, sc_wr.encode<64>(std::numeric_limits::min())); + ASSERT_EQ(1, sc_wr.encode<64>(std::numeric_limits::max())); + ASSERT_EQ(1, sc_wr.encode<15>((int16_t)-1)); + ASSERT_EQ(1, sc_wr.encode<2>((int16_t)-1)); + ASSERT_EQ(1, sc_wr.encode<16>(std::numeric_limits::min())); + ASSERT_EQ(1, sc_wr.encode<64>(std::numeric_limits::max())); // Total 302 bit (38 bytes) + + ASSERT_EQ(0, sc_wr.encode<64>(std::numeric_limits::min())); // Out of buffer space + + /* + * Decoding back + */ + uavcan::BitStream bs_rd(buf); + uavcan::ScalarCodec sc_rd(bs_rd); + + uint8_t u8 = 0; + int8_t i8 = 0; + uint16_t u16 = 0; + int16_t i16 = 0; + uint32_t u32 = 0; + int32_t i32 = 0; + uint64_t u64 = 0; + int64_t i64 = 0; + +#define CHECK(bitlen, variable, expected_value) \ + do { \ + ASSERT_EQ(1, sc_rd.decode(variable)); \ + ASSERT_EQ(expected_value, variable); \ + } while (0) + + CHECK(12, u16, 0xeda); + CHECK(1, u8, 1); + CHECK(1, u16, 0); + CHECK(4, u8, 8); + CHECK(32, u32, 0xdeadbeef); + CHECK(3, i8, -1); + CHECK(4, i8, -6); + CHECK(20, i32, -123456); + CHECK(64, i64, std::numeric_limits::min()); + CHECK(64, i64, std::numeric_limits::max()); + CHECK(15, i16, -1); + CHECK(2, i8, -1); + CHECK(16, i16, std::numeric_limits::min()); + CHECK(64, u64, std::numeric_limits::max()); + +#undef CHECK +} + +TEST(ScalarCodec, RepresentationCorrectness) +{ +}