blob: 955bce746e7a4108869b80ae008699ef41ee40bd [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
sprang@webrtc.org499631c2013-12-03 13:22:48 +000014// This file contains classes for reading and writing integer types from/to
15// byte array representations. Signed/unsigned, partial (whole byte) sizes,
16// and big/little endian byte order is all supported.
17//
18// Usage examples:
19//
20// uint8_t* buffer = ...;
21//
22// // Read an unsigned 4 byte integer in big endian format
23// uint32_t val = ByteReader<uint32_t>::ReadBigEndian(buffer);
24//
25// // Read a signed 24-bit (3 byte) integer in little endian format
26// int32_t val = ByteReader<int32_t, 3>::ReadLittle(buffer);
27//
28// // Write an unsigned 8 byte integer in little endian format
29// ByteWriter<uint64_t>::WriteLittleEndian(buffer, val);
30//
31// Write an unsigned 40-bit (5 byte) integer in big endian format
32// ByteWriter<uint64_t, 5>::WriteBigEndian(buffer, val);
33//
34// These classes are implemented as recursive templetizations, inteded to make
35// it easy for the compiler to completely inline the reading/writing.
36
Niels Möllera12c42a2018-07-25 16:05:48 +020037#include <stdint.h>
sprang@webrtc.org499631c2013-12-03 13:22:48 +000038#include <limits>
39
sprang@webrtc.org499631c2013-12-03 13:22:48 +000040namespace webrtc {
41
sprangbe9b7b62015-09-04 01:06:57 -070042// According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three
43// representations of signed integers allowed are two's complement, one's
44// complement and sign/magnitude. We can detect which is used by looking at
45// the two last bits of -1, which will be 11 in two's complement, 10 in one's
46// complement and 01 in sign/magnitude.
47// TODO(sprang): In the unlikely event that we actually need to support a
48// platform that doesn't use two's complement, implement conversion to/from
49// wire format.
50
danilchap6a6f0892015-12-10 12:39:08 -080051// Assume the if any one signed integer type is two's complement, then all
52// other will be too.
53static_assert(
54 (-1 & 0x03) == 0x03,
55 "Only two's complement representation of signed integers supported.");
56
sprangbe9b7b62015-09-04 01:06:57 -070057// Plain const char* won't work for static_assert, use #define instead.
58#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
sprangbe9b7b62015-09-04 01:06:57 -070059
60// Utility class for getting the unsigned equivalent of a signed type.
61template <typename T>
62struct UnsignedOf;
63
sprang@webrtc.org499631c2013-12-03 13:22:48 +000064// Class for reading integers from a sequence of bytes.
sprangbe9b7b62015-09-04 01:06:57 -070065// T = type of integer, B = bytes to read, is_signed = true if signed integer.
66// If is_signed is true and B < sizeof(T), sign extension might be needed.
67template <typename T,
68 unsigned int B = sizeof(T),
69 bool is_signed = std::numeric_limits<T>::is_signed>
70class ByteReader;
71
72// Specialization of ByteReader for unsigned types.
73template <typename T, unsigned int B>
74class ByteReader<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000075 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000076 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070077 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000078 return InternalReadBigEndian(data);
79 }
80
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000081 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070082 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000083 return InternalReadLittleEndian(data);
84 }
85
86 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000087 static T InternalReadBigEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000088 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070089 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000090 val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000091 return val;
92 }
93
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000094 static T InternalReadLittleEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000095 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070096 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000097 val |= static_cast<T>(data[i]) << (i * 8);
sprangbe9b7b62015-09-04 01:06:57 -070098 return val;
99 }
100};
101
102// Specialization of ByteReader for signed types.
103template <typename T, unsigned int B>
104class ByteReader<T, B, true> {
105 public:
106 typedef typename UnsignedOf<T>::Type U;
107
108 static T ReadBigEndian(const uint8_t* data) {
109 U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
110 if (B < sizeof(T))
111 unsigned_val = SignExtend(unsigned_val);
112 return ReinterpretAsSigned(unsigned_val);
113 }
114
115 static T ReadLittleEndian(const uint8_t* data) {
116 U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
117 if (B < sizeof(T))
118 unsigned_val = SignExtend(unsigned_val);
119 return ReinterpretAsSigned(unsigned_val);
120 }
121
122 private:
123 // As a hack to avoid implementation-specific or undefined behavior when
124 // bit-shifting or casting signed integers, read as a signed equivalent
125 // instead and convert to signed. This is safe since we have asserted that
126 // two's complement for is used.
127 static T ReinterpretAsSigned(U unsigned_val) {
128 // An unsigned value with only the highest order bit set (ex 0x80).
Yves Gerey665174f2018-06-19 15:03:05 +0200129 const U kUnsignedHighestBitMask = static_cast<U>(1)
130 << ((sizeof(U) * 8) - 1);
sprangbe9b7b62015-09-04 01:06:57 -0700131 // A signed value with only the highest bit set. Since this is two's
132 // complement form, we can use the min value from std::numeric_limits.
133 const T kSignedHighestBitMask = std::numeric_limits<T>::min();
134
135 T val;
136 if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
137 // Casting is only safe when unsigned value can be represented in the
138 // signed target type, so mask out highest bit and mask it back manually.
139 val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
140 val |= kSignedHighestBitMask;
141 } else {
142 val = static_cast<T>(unsigned_val);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000143 }
144 return val;
145 }
146
147 // If number of bytes is less than native data type (eg 24 bit, in int32_t),
148 // and the most significant bit of the actual data is set, we must sign
149 // extend the remaining byte(s) with ones so that the correct negative
150 // number is retained.
151 // Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
sprangbe9b7b62015-09-04 01:06:57 -0700152 static U SignExtend(const U val) {
153 const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
154 if ((kMsb & 0x80) != 0) {
155 // Create a mask where all bits used by the B bytes are set to one,
156 // for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
157 // (0xFF000000 in the example above) and add it to the input value.
158 // The "B % sizeof(T)" is a workaround to undefined values warnings for
159 // B == sizeof(T), in which case this code won't be called anyway.
160 const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
161 return ~kUsedBitsMask | val;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000162 }
163 return val;
164 }
165};
166
167// Class for writing integers to a sequence of bytes
168// T = type of integer, B = bytes to write
sprangbe9b7b62015-09-04 01:06:57 -0700169template <typename T,
170 unsigned int B = sizeof(T),
171 bool is_signed = std::numeric_limits<T>::is_signed>
172class ByteWriter;
173
174// Specialization of ByteWriter for unsigned types.
175template <typename T, unsigned int B>
176class ByteWriter<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000177 public:
178 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700179 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000180 for (unsigned int i = 0; i < B; ++i) {
181 data[i] = val >> ((B - 1 - i) * 8);
182 }
183 }
184
185 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700186 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000187 for (unsigned int i = 0; i < B; ++i) {
188 data[i] = val >> (i * 8);
189 }
190 }
191};
192
sprangbe9b7b62015-09-04 01:06:57 -0700193// Specialization of ByteWriter for signed types.
194template <typename T, unsigned int B>
195class ByteWriter<T, B, true> {
196 public:
197 typedef typename UnsignedOf<T>::Type U;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000198
sprangbe9b7b62015-09-04 01:06:57 -0700199 static void WriteBigEndian(uint8_t* data, T val) {
200 ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
201 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000202
sprangbe9b7b62015-09-04 01:06:57 -0700203 static void WriteLittleEndian(uint8_t* data, T val) {
204 ByteWriter<U, B, false>::WriteLittleEndian(data,
205 ReinterpretAsUnsigned(val));
206 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000207
sprangbe9b7b62015-09-04 01:06:57 -0700208 private:
209 static U ReinterpretAsUnsigned(T val) {
210 // According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
211 // conversion from signed to unsigned keeps the value if the new type can
212 // represent it, and otherwise adds one more than the max value of T until
213 // the value is in range. For two's complement, this fortunately means
214 // that the bit-wise value will be intact. Thus, since we have asserted that
215 // two's complement form is actually used, a simple cast is sufficient.
216 return static_cast<U>(val);
217 }
218};
219
220// ----- Below follows specializations of UnsignedOf utility class -----
221
222template <>
223struct UnsignedOf<int8_t> {
224 typedef uint8_t Type;
225};
226template <>
227struct UnsignedOf<int16_t> {
228 typedef uint16_t Type;
229};
230template <>
231struct UnsignedOf<int32_t> {
232 typedef uint32_t Type;
233};
234template <>
235struct UnsignedOf<int64_t> {
236 typedef uint64_t Type;
237};
238
239// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
240
241// TODO(sprang): Check if these actually help or if generic cases will be
242// unrolled to and optimized to similar performance.
243
244// Specializations for single bytes
245template <typename T>
246class ByteReader<T, 1, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000247 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000248 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700249 static_assert(sizeof(T) == 1, kSizeErrorMsg);
250 return data[0];
251 }
252
253 static T ReadLittleEndian(const uint8_t* data) {
254 static_assert(sizeof(T) == 1, kSizeErrorMsg);
255 return data[0];
256 }
257};
258
259template <typename T>
260class ByteWriter<T, 1, false> {
261 public:
262 static void WriteBigEndian(uint8_t* data, T val) {
263 static_assert(sizeof(T) == 1, kSizeErrorMsg);
264 data[0] = val;
265 }
266
267 static void WriteLittleEndian(uint8_t* data, T val) {
268 static_assert(sizeof(T) == 1, kSizeErrorMsg);
269 data[0] = val;
270 }
271};
272
273// Specializations for two byte words
274template <typename T>
275class ByteReader<T, 2, false> {
276 public:
277 static T ReadBigEndian(const uint8_t* data) {
278 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000279 return (data[0] << 8) | data[1];
280 }
281
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000282 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700283 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000284 return data[0] | (data[1] << 8);
285 }
286};
287
sprangbe9b7b62015-09-04 01:06:57 -0700288template <typename T>
289class ByteWriter<T, 2, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000290 public:
291 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700292 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000293 data[0] = val >> 8;
294 data[1] = val;
295 }
296
297 static void WriteLittleEndian(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;
300 data[1] = val >> 8;
301 }
302};
303
304// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700305template <typename T>
306class ByteReader<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000307 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000308 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700309 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800310 return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) |
311 Get(data, 3);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000312 }
313
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000314 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700315 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800316 return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
317 (Get(data, 3) << 24);
318 }
319
320 private:
321 inline static T Get(const uint8_t* data, unsigned int index) {
322 return static_cast<T>(data[index]);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000323 }
324};
325
326// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700327template <typename T>
328class ByteWriter<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000329 public:
330 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700331 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000332 data[0] = val >> 24;
333 data[1] = val >> 16;
334 data[2] = val >> 8;
335 data[3] = val;
336 }
337
338 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700339 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000340 data[0] = val;
341 data[1] = val >> 8;
342 data[2] = val >> 16;
343 data[3] = val >> 24;
344 }
345};
346
347// Specializations for eight byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700348template <typename T>
349class ByteReader<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000350 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000351 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700352 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
Yves Gerey665174f2018-06-19 15:03:05 +0200353 return (Get(data, 0) << 56) | (Get(data, 1) << 48) | (Get(data, 2) << 40) |
354 (Get(data, 3) << 32) | (Get(data, 4) << 24) | (Get(data, 5) << 16) |
355 (Get(data, 6) << 8) | Get(data, 7);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000356 }
357
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000358 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700359 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
Yves Gerey665174f2018-06-19 15:03:05 +0200360 return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
361 (Get(data, 3) << 24) | (Get(data, 4) << 32) | (Get(data, 5) << 40) |
362 (Get(data, 6) << 48) | (Get(data, 7) << 56);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000363 }
364
365 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000366 inline static T Get(const uint8_t* data, unsigned int index) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000367 return static_cast<T>(data[index]);
368 }
369};
370
sprangbe9b7b62015-09-04 01:06:57 -0700371template <typename T>
372class ByteWriter<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000373 public:
374 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700375 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000376 data[0] = val >> 56;
377 data[1] = val >> 48;
378 data[2] = val >> 40;
379 data[3] = val >> 32;
380 data[4] = val >> 24;
381 data[5] = val >> 16;
382 data[6] = val >> 8;
383 data[7] = val;
384 }
385
386 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700387 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000388 data[0] = val;
389 data[1] = val >> 8;
390 data[2] = val >> 16;
391 data[3] = val >> 24;
392 data[4] = val >> 32;
393 data[5] = val >> 40;
394 data[6] = val >> 48;
395 data[7] = val >> 56;
396 }
397};
398
399} // namespace webrtc
400
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200401#endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_