403 lines
13 KiB
C
403 lines
13 KiB
C
|
/*
|
||
|
* 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<uint32_t>::ReadBigEndian(buffer);
|
||
|
//
|
||
|
// // Read a signed 24-bit (3 byte) integer in little endian format
|
||
|
// int32_t val = ByteReader<int32_t, 3>::ReadLittle(buffer);
|
||
|
//
|
||
|
// // Write an unsigned 8 byte integer in little endian format
|
||
|
// ByteWriter<uint64_t>::WriteLittleEndian(buffer, val);
|
||
|
//
|
||
|
// Write an unsigned 40-bit (5 byte) integer in big endian format
|
||
|
// ByteWriter<uint64_t, 5>::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 <stdint.h>
|
||
|
|
||
|
#include <limits>
|
||
|
|
||
|
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 <typename T>
|
||
|
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 <typename T,
|
||
|
unsigned int B = sizeof(T),
|
||
|
bool is_signed = std::numeric_limits<T>::is_signed>
|
||
|
class ByteReader;
|
||
|
|
||
|
// Specialization of ByteReader for unsigned types.
|
||
|
template <typename T, unsigned int B>
|
||
|
class ByteReader<T, B, false> {
|
||
|
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<T>(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<T>(data[i]) << (i * 8);
|
||
|
return val;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Specialization of ByteReader for signed types.
|
||
|
template <typename T, unsigned int B>
|
||
|
class ByteReader<T, B, true> {
|
||
|
public:
|
||
|
typedef typename UnsignedOf<T>::Type U;
|
||
|
|
||
|
static T ReadBigEndian(const uint8_t* data) {
|
||
|
U unsigned_val = ByteReader<T, B, false>::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<T, B, false>::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<U>(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<T>::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<T>(unsigned_val & ~kUnsignedHighestBitMask);
|
||
|
val |= kSignedHighestBitMask;
|
||
|
} else {
|
||
|
val = static_cast<T>(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<uint8_t>(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 <typename T,
|
||
|
unsigned int B = sizeof(T),
|
||
|
bool is_signed = std::numeric_limits<T>::is_signed>
|
||
|
class ByteWriter;
|
||
|
|
||
|
// Specialization of ByteWriter for unsigned types.
|
||
|
template <typename T, unsigned int B>
|
||
|
class ByteWriter<T, B, false> {
|
||
|
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 <typename T, unsigned int B>
|
||
|
class ByteWriter<T, B, true> {
|
||
|
public:
|
||
|
typedef typename UnsignedOf<T>::Type U;
|
||
|
|
||
|
static void WriteBigEndian(uint8_t* data, T val) {
|
||
|
ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
|
||
|
}
|
||
|
|
||
|
static void WriteLittleEndian(uint8_t* data, T val) {
|
||
|
ByteWriter<U, B, false>::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<U>(val);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// ----- Below follows specializations of UnsignedOf utility class -----
|
||
|
|
||
|
template <>
|
||
|
struct UnsignedOf<int8_t> {
|
||
|
typedef uint8_t Type;
|
||
|
};
|
||
|
template <>
|
||
|
struct UnsignedOf<int16_t> {
|
||
|
typedef uint16_t Type;
|
||
|
};
|
||
|
template <>
|
||
|
struct UnsignedOf<int32_t> {
|
||
|
typedef uint32_t Type;
|
||
|
};
|
||
|
template <>
|
||
|
struct UnsignedOf<int64_t> {
|
||
|
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 <typename T>
|
||
|
class ByteReader<T, 1, false> {
|
||
|
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 <typename T>
|
||
|
class ByteWriter<T, 1, false> {
|
||
|
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 <typename T>
|
||
|
class ByteReader<T, 2, false> {
|
||
|
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 <typename T>
|
||
|
class ByteWriter<T, 2, false> {
|
||
|
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 <typename T>
|
||
|
class ByteReader<T, 4, false> {
|
||
|
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<T>(data[index]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Specializations for four byte words.
|
||
|
template <typename T>
|
||
|
class ByteWriter<T, 4, false> {
|
||
|
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 <typename T>
|
||
|
class ByteReader<T, 8, false> {
|
||
|
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<T>(data[index]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename T>
|
||
|
class ByteWriter<T, 8, false> {
|
||
|
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_
|