blob: e482452b9fdbca5fd5b0898acc5fb1a660ec0224 [file] [log] [blame]
sprang@webrtc.org499631c2013-12-03 13:22:48 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#ifndef MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
12#define MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
sprang@webrtc.org499631c2013-12-03 13:22:48 +000013
14
15// This file contains classes for reading and writing integer types from/to
16// byte array representations. Signed/unsigned, partial (whole byte) sizes,
17// and big/little endian byte order is all supported.
18//
19// Usage examples:
20//
21// uint8_t* buffer = ...;
22//
23// // Read an unsigned 4 byte integer in big endian format
24// uint32_t val = ByteReader<uint32_t>::ReadBigEndian(buffer);
25//
26// // Read a signed 24-bit (3 byte) integer in little endian format
27// int32_t val = ByteReader<int32_t, 3>::ReadLittle(buffer);
28//
29// // Write an unsigned 8 byte integer in little endian format
30// ByteWriter<uint64_t>::WriteLittleEndian(buffer, val);
31//
32// Write an unsigned 40-bit (5 byte) integer in big endian format
33// ByteWriter<uint64_t, 5>::WriteBigEndian(buffer, val);
34//
35// These classes are implemented as recursive templetizations, inteded to make
36// it easy for the compiler to completely inline the reading/writing.
37
38
39#include <limits>
40
Mirko Bonadei71207422017-09-15 13:58:09 +020041#include "typedefs.h" // NOLINT(build/include)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000042
43namespace webrtc {
44
sprangbe9b7b62015-09-04 01:06:57 -070045// According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three
46// representations of signed integers allowed are two's complement, one's
47// complement and sign/magnitude. We can detect which is used by looking at
48// the two last bits of -1, which will be 11 in two's complement, 10 in one's
49// complement and 01 in sign/magnitude.
50// TODO(sprang): In the unlikely event that we actually need to support a
51// platform that doesn't use two's complement, implement conversion to/from
52// wire format.
53
danilchap6a6f0892015-12-10 12:39:08 -080054// Assume the if any one signed integer type is two's complement, then all
55// other will be too.
56static_assert(
57 (-1 & 0x03) == 0x03,
58 "Only two's complement representation of signed integers supported.");
59
sprangbe9b7b62015-09-04 01:06:57 -070060// Plain const char* won't work for static_assert, use #define instead.
61#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
sprangbe9b7b62015-09-04 01:06:57 -070062
63// Utility class for getting the unsigned equivalent of a signed type.
64template <typename T>
65struct UnsignedOf;
66
sprang@webrtc.org499631c2013-12-03 13:22:48 +000067// Class for reading integers from a sequence of bytes.
sprangbe9b7b62015-09-04 01:06:57 -070068// T = type of integer, B = bytes to read, is_signed = true if signed integer.
69// If is_signed is true and B < sizeof(T), sign extension might be needed.
70template <typename T,
71 unsigned int B = sizeof(T),
72 bool is_signed = std::numeric_limits<T>::is_signed>
73class ByteReader;
74
75// Specialization of ByteReader for unsigned types.
76template <typename T, unsigned int B>
77class ByteReader<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000078 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000079 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070080 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000081 return InternalReadBigEndian(data);
82 }
83
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000084 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070085 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000086 return InternalReadLittleEndian(data);
87 }
88
89 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000090 static T InternalReadBigEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000091 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070092 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000093 val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000094 return val;
95 }
96
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000097 static T InternalReadLittleEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000098 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070099 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000100 val |= static_cast<T>(data[i]) << (i * 8);
sprangbe9b7b62015-09-04 01:06:57 -0700101 return val;
102 }
103};
104
105// Specialization of ByteReader for signed types.
106template <typename T, unsigned int B>
107class ByteReader<T, B, true> {
108 public:
109 typedef typename UnsignedOf<T>::Type U;
110
111 static T ReadBigEndian(const uint8_t* data) {
112 U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
113 if (B < sizeof(T))
114 unsigned_val = SignExtend(unsigned_val);
115 return ReinterpretAsSigned(unsigned_val);
116 }
117
118 static T ReadLittleEndian(const uint8_t* data) {
119 U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
120 if (B < sizeof(T))
121 unsigned_val = SignExtend(unsigned_val);
122 return ReinterpretAsSigned(unsigned_val);
123 }
124
125 private:
126 // As a hack to avoid implementation-specific or undefined behavior when
127 // bit-shifting or casting signed integers, read as a signed equivalent
128 // instead and convert to signed. This is safe since we have asserted that
129 // two's complement for is used.
130 static T ReinterpretAsSigned(U unsigned_val) {
131 // An unsigned value with only the highest order bit set (ex 0x80).
132 const U kUnsignedHighestBitMask =
133 static_cast<U>(1) << ((sizeof(U) * 8) - 1);
134 // A signed value with only the highest bit set. Since this is two's
135 // complement form, we can use the min value from std::numeric_limits.
136 const T kSignedHighestBitMask = std::numeric_limits<T>::min();
137
138 T val;
139 if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
140 // Casting is only safe when unsigned value can be represented in the
141 // signed target type, so mask out highest bit and mask it back manually.
142 val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
143 val |= kSignedHighestBitMask;
144 } else {
145 val = static_cast<T>(unsigned_val);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000146 }
147 return val;
148 }
149
150 // If number of bytes is less than native data type (eg 24 bit, in int32_t),
151 // and the most significant bit of the actual data is set, we must sign
152 // extend the remaining byte(s) with ones so that the correct negative
153 // number is retained.
154 // Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
sprangbe9b7b62015-09-04 01:06:57 -0700155 static U SignExtend(const U val) {
156 const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
157 if ((kMsb & 0x80) != 0) {
158 // Create a mask where all bits used by the B bytes are set to one,
159 // for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
160 // (0xFF000000 in the example above) and add it to the input value.
161 // The "B % sizeof(T)" is a workaround to undefined values warnings for
162 // B == sizeof(T), in which case this code won't be called anyway.
163 const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
164 return ~kUsedBitsMask | val;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000165 }
166 return val;
167 }
168};
169
170// Class for writing integers to a sequence of bytes
171// T = type of integer, B = bytes to write
sprangbe9b7b62015-09-04 01:06:57 -0700172template <typename T,
173 unsigned int B = sizeof(T),
174 bool is_signed = std::numeric_limits<T>::is_signed>
175class ByteWriter;
176
177// Specialization of ByteWriter for unsigned types.
178template <typename T, unsigned int B>
179class ByteWriter<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000180 public:
181 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700182 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000183 for (unsigned int i = 0; i < B; ++i) {
184 data[i] = val >> ((B - 1 - i) * 8);
185 }
186 }
187
188 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700189 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000190 for (unsigned int i = 0; i < B; ++i) {
191 data[i] = val >> (i * 8);
192 }
193 }
194};
195
sprangbe9b7b62015-09-04 01:06:57 -0700196// Specialization of ByteWriter for signed types.
197template <typename T, unsigned int B>
198class ByteWriter<T, B, true> {
199 public:
200 typedef typename UnsignedOf<T>::Type U;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000201
sprangbe9b7b62015-09-04 01:06:57 -0700202 static void WriteBigEndian(uint8_t* data, T val) {
203 ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
204 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000205
sprangbe9b7b62015-09-04 01:06:57 -0700206 static void WriteLittleEndian(uint8_t* data, T val) {
207 ByteWriter<U, B, false>::WriteLittleEndian(data,
208 ReinterpretAsUnsigned(val));
209 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000210
sprangbe9b7b62015-09-04 01:06:57 -0700211 private:
212 static U ReinterpretAsUnsigned(T val) {
213 // According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
214 // conversion from signed to unsigned keeps the value if the new type can
215 // represent it, and otherwise adds one more than the max value of T until
216 // the value is in range. For two's complement, this fortunately means
217 // that the bit-wise value will be intact. Thus, since we have asserted that
218 // two's complement form is actually used, a simple cast is sufficient.
219 return static_cast<U>(val);
220 }
221};
222
223// ----- Below follows specializations of UnsignedOf utility class -----
224
225template <>
226struct UnsignedOf<int8_t> {
227 typedef uint8_t Type;
228};
229template <>
230struct UnsignedOf<int16_t> {
231 typedef uint16_t Type;
232};
233template <>
234struct UnsignedOf<int32_t> {
235 typedef uint32_t Type;
236};
237template <>
238struct UnsignedOf<int64_t> {
239 typedef uint64_t Type;
240};
241
242// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
243
244// TODO(sprang): Check if these actually help or if generic cases will be
245// unrolled to and optimized to similar performance.
246
247// Specializations for single bytes
248template <typename T>
249class ByteReader<T, 1, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000250 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000251 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700252 static_assert(sizeof(T) == 1, kSizeErrorMsg);
253 return data[0];
254 }
255
256 static T ReadLittleEndian(const uint8_t* data) {
257 static_assert(sizeof(T) == 1, kSizeErrorMsg);
258 return data[0];
259 }
260};
261
262template <typename T>
263class ByteWriter<T, 1, false> {
264 public:
265 static void WriteBigEndian(uint8_t* data, T val) {
266 static_assert(sizeof(T) == 1, kSizeErrorMsg);
267 data[0] = val;
268 }
269
270 static void WriteLittleEndian(uint8_t* data, T val) {
271 static_assert(sizeof(T) == 1, kSizeErrorMsg);
272 data[0] = val;
273 }
274};
275
276// Specializations for two byte words
277template <typename T>
278class ByteReader<T, 2, false> {
279 public:
280 static T ReadBigEndian(const uint8_t* data) {
281 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000282 return (data[0] << 8) | data[1];
283 }
284
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000285 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700286 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000287 return data[0] | (data[1] << 8);
288 }
289};
290
sprangbe9b7b62015-09-04 01:06:57 -0700291template <typename T>
292class ByteWriter<T, 2, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000293 public:
294 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700295 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000296 data[0] = val >> 8;
297 data[1] = val;
298 }
299
300 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700301 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000302 data[0] = val;
303 data[1] = val >> 8;
304 }
305};
306
307// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700308template <typename T>
309class ByteReader<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000310 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000311 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700312 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800313 return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) |
314 Get(data, 3);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000315 }
316
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000317 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700318 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800319 return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
320 (Get(data, 3) << 24);
321 }
322
323 private:
324 inline static T Get(const uint8_t* data, unsigned int index) {
325 return static_cast<T>(data[index]);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000326 }
327};
328
329// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700330template <typename T>
331class ByteWriter<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000332 public:
333 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700334 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000335 data[0] = val >> 24;
336 data[1] = val >> 16;
337 data[2] = val >> 8;
338 data[3] = val;
339 }
340
341 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700342 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000343 data[0] = val;
344 data[1] = val >> 8;
345 data[2] = val >> 16;
346 data[3] = val >> 24;
347 }
348};
349
350// Specializations for eight byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700351template <typename T>
352class ByteReader<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000353 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000354 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700355 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000356 return
357 (Get(data, 0) << 56) | (Get(data, 1) << 48) |
358 (Get(data, 2) << 40) | (Get(data, 3) << 32) |
359 (Get(data, 4) << 24) | (Get(data, 5) << 16) |
360 (Get(data, 6) << 8) | Get(data, 7);
361 }
362
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000363 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700364 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000365 return
366 Get(data, 0) | (Get(data, 1) << 8) |
367 (Get(data, 2) << 16) | (Get(data, 3) << 24) |
368 (Get(data, 4) << 32) | (Get(data, 5) << 40) |
369 (Get(data, 6) << 48) | (Get(data, 7) << 56);
370 }
371
372 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000373 inline static T Get(const uint8_t* data, unsigned int index) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000374 return static_cast<T>(data[index]);
375 }
376};
377
sprangbe9b7b62015-09-04 01:06:57 -0700378template <typename T>
379class ByteWriter<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000380 public:
381 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700382 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000383 data[0] = val >> 56;
384 data[1] = val >> 48;
385 data[2] = val >> 40;
386 data[3] = val >> 32;
387 data[4] = val >> 24;
388 data[5] = val >> 16;
389 data[6] = val >> 8;
390 data[7] = val;
391 }
392
393 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700394 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000395 data[0] = val;
396 data[1] = val >> 8;
397 data[2] = val >> 16;
398 data[3] = val >> 24;
399 data[4] = val >> 32;
400 data[5] = val >> 40;
401 data[6] = val >> 48;
402 data[7] = val >> 56;
403 }
404};
405
406} // namespace webrtc
407
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200408#endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_