blob: d8903b8483cf929ad532adbd388c2e3ff286847c [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
11#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
12#define WEBRTC_MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_
13
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
41#include "webrtc/typedefs.h"
42
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
54namespace {
55inline void AssertTwosComplement() {
56 // Assume the if any one signed integer type is two's complement, then all
57 // other will be too.
58 static_assert(
59 (-1 & 0x03) == 0x03,
60 "Only two's complement representation of signed integers supported.");
61}
62// Plain const char* won't work for static_assert, use #define instead.
63#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
64}
65
66// Utility class for getting the unsigned equivalent of a signed type.
67template <typename T>
68struct UnsignedOf;
69
sprang@webrtc.org499631c2013-12-03 13:22:48 +000070// Class for reading integers from a sequence of bytes.
sprangbe9b7b62015-09-04 01:06:57 -070071// T = type of integer, B = bytes to read, is_signed = true if signed integer.
72// If is_signed is true and B < sizeof(T), sign extension might be needed.
73template <typename T,
74 unsigned int B = sizeof(T),
75 bool is_signed = std::numeric_limits<T>::is_signed>
76class ByteReader;
77
78// Specialization of ByteReader for unsigned types.
79template <typename T, unsigned int B>
80class ByteReader<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000081 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000082 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070083 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000084 return InternalReadBigEndian(data);
85 }
86
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000087 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070088 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000089 return InternalReadLittleEndian(data);
90 }
91
92 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000093 static T InternalReadBigEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000094 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070095 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000096 val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000097 return val;
98 }
99
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000100 static T InternalReadLittleEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000101 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -0700102 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000103 val |= static_cast<T>(data[i]) << (i * 8);
sprangbe9b7b62015-09-04 01:06:57 -0700104 return val;
105 }
106};
107
108// Specialization of ByteReader for signed types.
109template <typename T, unsigned int B>
110class ByteReader<T, B, true> {
111 public:
112 typedef typename UnsignedOf<T>::Type U;
113
114 static T ReadBigEndian(const uint8_t* data) {
115 U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
116 if (B < sizeof(T))
117 unsigned_val = SignExtend(unsigned_val);
118 return ReinterpretAsSigned(unsigned_val);
119 }
120
121 static T ReadLittleEndian(const uint8_t* data) {
122 U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
123 if (B < sizeof(T))
124 unsigned_val = SignExtend(unsigned_val);
125 return ReinterpretAsSigned(unsigned_val);
126 }
127
128 private:
129 // As a hack to avoid implementation-specific or undefined behavior when
130 // bit-shifting or casting signed integers, read as a signed equivalent
131 // instead and convert to signed. This is safe since we have asserted that
132 // two's complement for is used.
133 static T ReinterpretAsSigned(U unsigned_val) {
134 // An unsigned value with only the highest order bit set (ex 0x80).
135 const U kUnsignedHighestBitMask =
136 static_cast<U>(1) << ((sizeof(U) * 8) - 1);
137 // A signed value with only the highest bit set. Since this is two's
138 // complement form, we can use the min value from std::numeric_limits.
139 const T kSignedHighestBitMask = std::numeric_limits<T>::min();
140
141 T val;
142 if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
143 // Casting is only safe when unsigned value can be represented in the
144 // signed target type, so mask out highest bit and mask it back manually.
145 val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
146 val |= kSignedHighestBitMask;
147 } else {
148 val = static_cast<T>(unsigned_val);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000149 }
150 return val;
151 }
152
153 // If number of bytes is less than native data type (eg 24 bit, in int32_t),
154 // and the most significant bit of the actual data is set, we must sign
155 // extend the remaining byte(s) with ones so that the correct negative
156 // number is retained.
157 // Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
sprangbe9b7b62015-09-04 01:06:57 -0700158 static U SignExtend(const U val) {
159 const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
160 if ((kMsb & 0x80) != 0) {
161 // Create a mask where all bits used by the B bytes are set to one,
162 // for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
163 // (0xFF000000 in the example above) and add it to the input value.
164 // The "B % sizeof(T)" is a workaround to undefined values warnings for
165 // B == sizeof(T), in which case this code won't be called anyway.
166 const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
167 return ~kUsedBitsMask | val;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000168 }
169 return val;
170 }
171};
172
173// Class for writing integers to a sequence of bytes
174// T = type of integer, B = bytes to write
sprangbe9b7b62015-09-04 01:06:57 -0700175template <typename T,
176 unsigned int B = sizeof(T),
177 bool is_signed = std::numeric_limits<T>::is_signed>
178class ByteWriter;
179
180// Specialization of ByteWriter for unsigned types.
181template <typename T, unsigned int B>
182class ByteWriter<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000183 public:
184 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700185 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000186 for (unsigned int i = 0; i < B; ++i) {
187 data[i] = val >> ((B - 1 - i) * 8);
188 }
189 }
190
191 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700192 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000193 for (unsigned int i = 0; i < B; ++i) {
194 data[i] = val >> (i * 8);
195 }
196 }
197};
198
sprangbe9b7b62015-09-04 01:06:57 -0700199// Specialization of ByteWriter for signed types.
200template <typename T, unsigned int B>
201class ByteWriter<T, B, true> {
202 public:
203 typedef typename UnsignedOf<T>::Type U;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000204
sprangbe9b7b62015-09-04 01:06:57 -0700205 static void WriteBigEndian(uint8_t* data, T val) {
206 ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
207 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000208
sprangbe9b7b62015-09-04 01:06:57 -0700209 static void WriteLittleEndian(uint8_t* data, T val) {
210 ByteWriter<U, B, false>::WriteLittleEndian(data,
211 ReinterpretAsUnsigned(val));
212 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000213
sprangbe9b7b62015-09-04 01:06:57 -0700214 private:
215 static U ReinterpretAsUnsigned(T val) {
216 // According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
217 // conversion from signed to unsigned keeps the value if the new type can
218 // represent it, and otherwise adds one more than the max value of T until
219 // the value is in range. For two's complement, this fortunately means
220 // that the bit-wise value will be intact. Thus, since we have asserted that
221 // two's complement form is actually used, a simple cast is sufficient.
222 return static_cast<U>(val);
223 }
224};
225
226// ----- Below follows specializations of UnsignedOf utility class -----
227
228template <>
229struct UnsignedOf<int8_t> {
230 typedef uint8_t Type;
231};
232template <>
233struct UnsignedOf<int16_t> {
234 typedef uint16_t Type;
235};
236template <>
237struct UnsignedOf<int32_t> {
238 typedef uint32_t Type;
239};
240template <>
241struct UnsignedOf<int64_t> {
242 typedef uint64_t Type;
243};
244
245// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
246
247// TODO(sprang): Check if these actually help or if generic cases will be
248// unrolled to and optimized to similar performance.
249
250// Specializations for single bytes
251template <typename T>
252class ByteReader<T, 1, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000253 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000254 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700255 static_assert(sizeof(T) == 1, kSizeErrorMsg);
256 return data[0];
257 }
258
259 static T ReadLittleEndian(const uint8_t* data) {
260 static_assert(sizeof(T) == 1, kSizeErrorMsg);
261 return data[0];
262 }
263};
264
265template <typename T>
266class ByteWriter<T, 1, false> {
267 public:
268 static void WriteBigEndian(uint8_t* data, T val) {
269 static_assert(sizeof(T) == 1, kSizeErrorMsg);
270 data[0] = val;
271 }
272
273 static void WriteLittleEndian(uint8_t* data, T val) {
274 static_assert(sizeof(T) == 1, kSizeErrorMsg);
275 data[0] = val;
276 }
277};
278
279// Specializations for two byte words
280template <typename T>
281class ByteReader<T, 2, false> {
282 public:
283 static T ReadBigEndian(const uint8_t* data) {
284 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000285 return (data[0] << 8) | data[1];
286 }
287
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000288 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700289 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000290 return data[0] | (data[1] << 8);
291 }
292};
293
sprangbe9b7b62015-09-04 01:06:57 -0700294template <typename T>
295class ByteWriter<T, 2, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000296 public:
297 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700298 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000299 data[0] = val >> 8;
300 data[1] = val;
301 }
302
303 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700304 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000305 data[0] = val;
306 data[1] = val >> 8;
307 }
308};
309
310// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700311template <typename T>
312class ByteReader<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000313 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000314 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700315 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000316 return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
317 }
318
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000319 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700320 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000321 return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
322 }
323};
324
325// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700326template <typename T>
327class ByteWriter<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000328 public:
329 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700330 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000331 data[0] = val >> 24;
332 data[1] = val >> 16;
333 data[2] = val >> 8;
334 data[3] = val;
335 }
336
337 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700338 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000339 data[0] = val;
340 data[1] = val >> 8;
341 data[2] = val >> 16;
342 data[3] = val >> 24;
343 }
344};
345
346// Specializations for eight byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700347template <typename T>
348class ByteReader<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000349 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000350 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700351 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000352 return
353 (Get(data, 0) << 56) | (Get(data, 1) << 48) |
354 (Get(data, 2) << 40) | (Get(data, 3) << 32) |
355 (Get(data, 4) << 24) | (Get(data, 5) << 16) |
356 (Get(data, 6) << 8) | Get(data, 7);
357 }
358
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000359 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700360 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000361 return
362 Get(data, 0) | (Get(data, 1) << 8) |
363 (Get(data, 2) << 16) | (Get(data, 3) << 24) |
364 (Get(data, 4) << 32) | (Get(data, 5) << 40) |
365 (Get(data, 6) << 48) | (Get(data, 7) << 56);
366 }
367
368 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000369 inline static T Get(const uint8_t* data, unsigned int index) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000370 return static_cast<T>(data[index]);
371 }
372};
373
sprangbe9b7b62015-09-04 01:06:57 -0700374template <typename T>
375class ByteWriter<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000376 public:
377 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700378 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000379 data[0] = val >> 56;
380 data[1] = val >> 48;
381 data[2] = val >> 40;
382 data[3] = val >> 32;
383 data[4] = val >> 24;
384 data[5] = val >> 16;
385 data[6] = val >> 8;
386 data[7] = val;
387 }
388
389 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700390 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000391 data[0] = val;
392 data[1] = val >> 8;
393 data[2] = val >> 16;
394 data[3] = val >> 24;
395 data[4] = val >> 32;
396 data[5] = val >> 40;
397 data[6] = val >> 48;
398 data[7] = val >> 56;
399 }
400};
401
402} // namespace webrtc
403
404#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_