/* * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_ #define MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_ // This file contains classes for reading and writing integer types from/to // byte array representations. Signed/unsigned, partial (whole byte) sizes, // and big/little endian byte order is all supported. // // Usage examples: // // uint8_t* buffer = ...; // // // Read an unsigned 4 byte integer in big endian format // uint32_t val = ByteReader::ReadBigEndian(buffer); // // // Read a signed 24-bit (3 byte) integer in little endian format // int32_t val = ByteReader::ReadLittle(buffer); // // // Write an unsigned 8 byte integer in little endian format // ByteWriter::WriteLittleEndian(buffer, val); // // Write an unsigned 40-bit (5 byte) integer in big endian format // ByteWriter::WriteBigEndian(buffer, val); // // These classes are implemented as recursive templetizations, inteded to make // it easy for the compiler to completely inline the reading/writing. #include #include namespace webrtc { // According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three // representations of signed integers allowed are two's complement, one's // complement and sign/magnitude. We can detect which is used by looking at // the two last bits of -1, which will be 11 in two's complement, 10 in one's // complement and 01 in sign/magnitude. // TODO(sprang): In the unlikely event that we actually need to support a // platform that doesn't use two's complement, implement conversion to/from // wire format. // Assume the if any one signed integer type is two's complement, then all // other will be too. static_assert( (-1 & 0x03) == 0x03, "Only two's complement representation of signed integers supported."); // Plain const char* won't work for static_assert, use #define instead. #define kSizeErrorMsg "Byte size must be less than or equal to data type size." // Utility class for getting the unsigned equivalent of a signed type. template struct UnsignedOf; // Class for reading integers from a sequence of bytes. // T = type of integer, B = bytes to read, is_signed = true if signed integer. // If is_signed is true and B < sizeof(T), sign extension might be needed. template ::is_signed> class ByteReader; // Specialization of ByteReader for unsigned types. template class ByteReader { public: static T ReadBigEndian(const uint8_t* data) { static_assert(B <= sizeof(T), kSizeErrorMsg); return InternalReadBigEndian(data); } static T ReadLittleEndian(const uint8_t* data) { static_assert(B <= sizeof(T), kSizeErrorMsg); return InternalReadLittleEndian(data); } private: static T InternalReadBigEndian(const uint8_t* data) { T val(0); for (unsigned int i = 0; i < B; ++i) val |= static_cast(data[i]) << ((B - 1 - i) * 8); return val; } static T InternalReadLittleEndian(const uint8_t* data) { T val(0); for (unsigned int i = 0; i < B; ++i) val |= static_cast(data[i]) << (i * 8); return val; } }; // Specialization of ByteReader for signed types. template class ByteReader { public: typedef typename UnsignedOf::Type U; static T ReadBigEndian(const uint8_t* data) { U unsigned_val = ByteReader::ReadBigEndian(data); if (B < sizeof(T)) unsigned_val = SignExtend(unsigned_val); return ReinterpretAsSigned(unsigned_val); } static T ReadLittleEndian(const uint8_t* data) { U unsigned_val = ByteReader::ReadLittleEndian(data); if (B < sizeof(T)) unsigned_val = SignExtend(unsigned_val); return ReinterpretAsSigned(unsigned_val); } private: // As a hack to avoid implementation-specific or undefined behavior when // bit-shifting or casting signed integers, read as a signed equivalent // instead and convert to signed. This is safe since we have asserted that // two's complement for is used. static T ReinterpretAsSigned(U unsigned_val) { // An unsigned value with only the highest order bit set (ex 0x80). const U kUnsignedHighestBitMask = static_cast(1) << ((sizeof(U) * 8) - 1); // A signed value with only the highest bit set. Since this is two's // complement form, we can use the min value from std::numeric_limits. const T kSignedHighestBitMask = std::numeric_limits::min(); T val; if ((unsigned_val & kUnsignedHighestBitMask) != 0) { // Casting is only safe when unsigned value can be represented in the // signed target type, so mask out highest bit and mask it back manually. val = static_cast(unsigned_val & ~kUnsignedHighestBitMask); val |= kSignedHighestBitMask; } else { val = static_cast(unsigned_val); } return val; } // If number of bytes is less than native data type (eg 24 bit, in int32_t), // and the most significant bit of the actual data is set, we must sign // extend the remaining byte(s) with ones so that the correct negative // number is retained. // Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B static U SignExtend(const U val) { const uint8_t kMsb = static_cast(val >> ((B - 1) * 8)); if ((kMsb & 0x80) != 0) { // Create a mask where all bits used by the B bytes are set to one, // for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to // (0xFF000000 in the example above) and add it to the input value. // The "B % sizeof(T)" is a workaround to undefined values warnings for // B == sizeof(T), in which case this code won't be called anyway. const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1; return ~kUsedBitsMask | val; } return val; } }; // Class for writing integers to a sequence of bytes // T = type of integer, B = bytes to write template ::is_signed> class ByteWriter; // Specialization of ByteWriter for unsigned types. template class ByteWriter { public: static void WriteBigEndian(uint8_t* data, T val) { static_assert(B <= sizeof(T), kSizeErrorMsg); for (unsigned int i = 0; i < B; ++i) { data[i] = val >> ((B - 1 - i) * 8); } } static void WriteLittleEndian(uint8_t* data, T val) { static_assert(B <= sizeof(T), kSizeErrorMsg); for (unsigned int i = 0; i < B; ++i) { data[i] = val >> (i * 8); } } }; // Specialization of ByteWriter for signed types. template class ByteWriter { public: typedef typename UnsignedOf::Type U; static void WriteBigEndian(uint8_t* data, T val) { ByteWriter::WriteBigEndian(data, ReinterpretAsUnsigned(val)); } static void WriteLittleEndian(uint8_t* data, T val) { ByteWriter::WriteLittleEndian(data, ReinterpretAsUnsigned(val)); } private: static U ReinterpretAsUnsigned(T val) { // According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a // conversion from signed to unsigned keeps the value if the new type can // represent it, and otherwise adds one more than the max value of T until // the value is in range. For two's complement, this fortunately means // that the bit-wise value will be intact. Thus, since we have asserted that // two's complement form is actually used, a simple cast is sufficient. return static_cast(val); } }; // ----- Below follows specializations of UnsignedOf utility class ----- template <> struct UnsignedOf { typedef uint8_t Type; }; template <> struct UnsignedOf { typedef uint16_t Type; }; template <> struct UnsignedOf { typedef uint32_t Type; }; template <> struct UnsignedOf { typedef uint64_t Type; }; // ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } ----- // TODO(sprang): Check if these actually help or if generic cases will be // unrolled to and optimized to similar performance. // Specializations for single bytes template class ByteReader { public: static T ReadBigEndian(const uint8_t* data) { static_assert(sizeof(T) == 1, kSizeErrorMsg); return data[0]; } static T ReadLittleEndian(const uint8_t* data) { static_assert(sizeof(T) == 1, kSizeErrorMsg); return data[0]; } }; template class ByteWriter { public: static void WriteBigEndian(uint8_t* data, T val) { static_assert(sizeof(T) == 1, kSizeErrorMsg); data[0] = val; } static void WriteLittleEndian(uint8_t* data, T val) { static_assert(sizeof(T) == 1, kSizeErrorMsg); data[0] = val; } }; // Specializations for two byte words template class ByteReader { public: static T ReadBigEndian(const uint8_t* data) { static_assert(sizeof(T) >= 2, kSizeErrorMsg); return (data[0] << 8) | data[1]; } static T ReadLittleEndian(const uint8_t* data) { static_assert(sizeof(T) >= 2, kSizeErrorMsg); return data[0] | (data[1] << 8); } }; template class ByteWriter { public: static void WriteBigEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 2, kSizeErrorMsg); data[0] = val >> 8; data[1] = val; } static void WriteLittleEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 2, kSizeErrorMsg); data[0] = val; data[1] = val >> 8; } }; // Specializations for four byte words. template class ByteReader { public: static T ReadBigEndian(const uint8_t* data) { static_assert(sizeof(T) >= 4, kSizeErrorMsg); return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) | Get(data, 3); } static T ReadLittleEndian(const uint8_t* data) { static_assert(sizeof(T) >= 4, kSizeErrorMsg); return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) | (Get(data, 3) << 24); } private: inline static T Get(const uint8_t* data, unsigned int index) { return static_cast(data[index]); } }; // Specializations for four byte words. template class ByteWriter { public: static void WriteBigEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 4, kSizeErrorMsg); data[0] = val >> 24; data[1] = val >> 16; data[2] = val >> 8; data[3] = val; } static void WriteLittleEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 4, kSizeErrorMsg); data[0] = val; data[1] = val >> 8; data[2] = val >> 16; data[3] = val >> 24; } }; // Specializations for eight byte words. template class ByteReader { public: static T ReadBigEndian(const uint8_t* data) { static_assert(sizeof(T) >= 8, kSizeErrorMsg); return (Get(data, 0) << 56) | (Get(data, 1) << 48) | (Get(data, 2) << 40) | (Get(data, 3) << 32) | (Get(data, 4) << 24) | (Get(data, 5) << 16) | (Get(data, 6) << 8) | Get(data, 7); } static T ReadLittleEndian(const uint8_t* data) { static_assert(sizeof(T) >= 8, kSizeErrorMsg); return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) | (Get(data, 3) << 24) | (Get(data, 4) << 32) | (Get(data, 5) << 40) | (Get(data, 6) << 48) | (Get(data, 7) << 56); } private: inline static T Get(const uint8_t* data, unsigned int index) { return static_cast(data[index]); } }; template class ByteWriter { public: static void WriteBigEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 8, kSizeErrorMsg); data[0] = val >> 56; data[1] = val >> 48; data[2] = val >> 40; data[3] = val >> 32; data[4] = val >> 24; data[5] = val >> 16; data[6] = val >> 8; data[7] = val; } static void WriteLittleEndian(uint8_t* data, T val) { static_assert(sizeof(T) >= 8, kSizeErrorMsg); data[0] = val; data[1] = val >> 8; data[2] = val >> 16; data[3] = val >> 24; data[4] = val >> 32; data[5] = val >> 40; data[6] = val >> 48; data[7] = val >> 56; } }; } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_