Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers scalar_codec.hpp Source File

scalar_codec.hpp

00001 /*
00002  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #ifndef UAVCAN_MARSHAL_SCALAR_CODEC_HPP_INCLUDED
00006 #define UAVCAN_MARSHAL_SCALAR_CODEC_HPP_INCLUDED
00007 
00008 #include <cassert>
00009 #include <uavcan/std.hpp>
00010 #include <uavcan/build_config.hpp>
00011 #include <uavcan/util/templates.hpp>
00012 #include <uavcan/marshal/bit_stream.hpp>
00013 
00014 namespace uavcan
00015 {
00016 /**
00017  * This class implements fast encoding/decoding of primitive type scalars into/from bit arrays.
00018  * It uses the compile-time type information to eliminate run-time operations where possible.
00019  */
00020 class UAVCAN_EXPORT ScalarCodec
00021 {
00022     BitStream& stream_;
00023 
00024     static void swapByteOrder(uint8_t* bytes, unsigned len);
00025 
00026     template <unsigned BitLen, unsigned Size>
00027     static typename EnableIf<(BitLen > 8)>::Type
00028     convertByteOrder(uint8_t (&bytes)[Size])
00029     {
00030 #if defined(BYTE_ORDER) && defined(BIG_ENDIAN)
00031         static const bool big_endian = BYTE_ORDER == BIG_ENDIAN;
00032 #else
00033         union { long int l; char c[sizeof(long int)]; } u;
00034         u.l = 1;
00035         const bool big_endian = u.c[sizeof(long int) - 1] == 1;
00036 #endif
00037         /*
00038          * I didn't have any big endian machine nearby, so big endian support wasn't tested yet.
00039          * It is likely to be OK anyway, so feel free to remove this UAVCAN_ASSERT() as needed.
00040          */
00041         UAVCAN_ASSERT(big_endian == false);
00042         if (big_endian)
00043         {
00044             swapByteOrder(bytes, Size);
00045         }
00046     }
00047 
00048     template <unsigned BitLen, unsigned Size>
00049     static typename EnableIf<(BitLen <= 8)>::Type
00050     convertByteOrder(uint8_t (&)[Size]) { }
00051 
00052     template <unsigned BitLen, typename T>
00053     static typename EnableIf<static_cast<bool>(NumericTraits<T>::IsSigned) && ((sizeof(T) * 8) > BitLen)>::Type
00054     fixTwosComplement(T& value)
00055     {
00056         StaticAssert<NumericTraits<T>::IsInteger>::check(); // Not applicable to floating point types
00057         if (value & (T(1) << (BitLen - 1)))                        // The most significant bit is set --> negative
00058         {
00059             value |= T(T(0xFFFFFFFFFFFFFFFFULL) & ~((T(1) << BitLen) - 1));
00060         }
00061     }
00062 
00063     template <unsigned BitLen, typename T>
00064     static typename EnableIf<!static_cast<bool>(NumericTraits<T>::IsSigned) || ((sizeof(T) * 8) == BitLen)>::Type
00065     fixTwosComplement(T&) { }
00066 
00067     template <unsigned BitLen, typename T>
00068     static typename EnableIf<((sizeof(T) * 8) > BitLen)>::Type
00069     clearExtraBits(T& value)
00070     {
00071         value &= (T(1) << BitLen) - 1;  // Signedness doesn't matter
00072     }
00073 
00074     template <unsigned BitLen, typename T>
00075     static typename EnableIf<((sizeof(T) * 8) == BitLen)>::Type
00076     clearExtraBits(T&) { }
00077 
00078     template <unsigned BitLen, typename T>
00079     void validate()
00080     {
00081         StaticAssert<((sizeof(T) * 8) >= BitLen)>::check();
00082         StaticAssert<(BitLen <= BitStream::MaxBitsPerRW)>::check();
00083         StaticAssert<static_cast<bool>(NumericTraits<T>::IsSigned) ? (BitLen > 1) : true>::check();
00084     }
00085 
00086     int encodeBytesImpl(uint8_t* bytes, unsigned bitlen);
00087     int decodeBytesImpl(uint8_t* bytes, unsigned bitlen);
00088 
00089 public:
00090     explicit ScalarCodec(BitStream& stream)
00091         : stream_(stream)
00092     { }
00093 
00094     template <unsigned BitLen, typename T>
00095     int encode(const T value);
00096 
00097     template <unsigned BitLen, typename T>
00098     int decode(T& value);
00099 };
00100 
00101 // ----------------------------------------------------------------------------
00102 
00103 template <unsigned BitLen, typename T>
00104 int ScalarCodec::encode(const T value)
00105 {
00106     validate<BitLen, T>();
00107     union ByteUnion
00108     {
00109         T value;
00110         uint8_t bytes[sizeof(T)];
00111     } byte_union;
00112     byte_union.value = value;
00113     clearExtraBits<BitLen, T>(byte_union.value);
00114     convertByteOrder<BitLen>(byte_union.bytes);
00115     return encodeBytesImpl(byte_union.bytes, BitLen);
00116 }
00117 
00118 template <unsigned BitLen, typename T>
00119 int ScalarCodec::decode(T& value)
00120 {
00121     validate<BitLen, T>();
00122     union ByteUnion
00123     {
00124         T value;
00125         uint8_t bytes[sizeof(T)];
00126     } byte_union;
00127     byte_union.value = T();
00128     const int read_res = decodeBytesImpl(byte_union.bytes, BitLen);
00129     if (read_res > 0)
00130     {
00131         convertByteOrder<BitLen>(byte_union.bytes);
00132         fixTwosComplement<BitLen, T>(byte_union.value);
00133         value = byte_union.value;
00134     }
00135     return read_res;
00136 }
00137 
00138 }
00139 
00140 #endif // UAVCAN_MARSHAL_SCALAR_CODEC_HPP_INCLUDED