blob: f9d973b0fe93b5ea9ad68795dcfbf3789ebd0039 [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
sprang@webrtc.org499631c2013-12-03 13:22:48 +000037#include <limits>
38
Mirko Bonadei71207422017-09-15 13:58:09 +020039#include "typedefs.h" // NOLINT(build/include)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000040
41namespace webrtc {
42
sprangbe9b7b62015-09-04 01:06:57 -070043// According to ISO C standard ISO/IEC 9899, section 6.2.6.2 (2), the three
44// representations of signed integers allowed are two's complement, one's
45// complement and sign/magnitude. We can detect which is used by looking at
46// the two last bits of -1, which will be 11 in two's complement, 10 in one's
47// complement and 01 in sign/magnitude.
48// TODO(sprang): In the unlikely event that we actually need to support a
49// platform that doesn't use two's complement, implement conversion to/from
50// wire format.
51
danilchap6a6f0892015-12-10 12:39:08 -080052// Assume the if any one signed integer type is two's complement, then all
53// other will be too.
54static_assert(
55 (-1 & 0x03) == 0x03,
56 "Only two's complement representation of signed integers supported.");
57
sprangbe9b7b62015-09-04 01:06:57 -070058// Plain const char* won't work for static_assert, use #define instead.
59#define kSizeErrorMsg "Byte size must be less than or equal to data type size."
sprangbe9b7b62015-09-04 01:06:57 -070060
61// Utility class for getting the unsigned equivalent of a signed type.
62template <typename T>
63struct UnsignedOf;
64
sprang@webrtc.org499631c2013-12-03 13:22:48 +000065// Class for reading integers from a sequence of bytes.
sprangbe9b7b62015-09-04 01:06:57 -070066// T = type of integer, B = bytes to read, is_signed = true if signed integer.
67// If is_signed is true and B < sizeof(T), sign extension might be needed.
68template <typename T,
69 unsigned int B = sizeof(T),
70 bool is_signed = std::numeric_limits<T>::is_signed>
71class ByteReader;
72
73// Specialization of ByteReader for unsigned types.
74template <typename T, unsigned int B>
75class ByteReader<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000076 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000077 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -070078 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000079 return InternalReadBigEndian(data);
80 }
81
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000082 static T ReadLittleEndian(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 InternalReadLittleEndian(data);
85 }
86
87 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000088 static T InternalReadBigEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000089 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070090 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000091 val |= static_cast<T>(data[i]) << ((B - 1 - i) * 8);
sprang@webrtc.org499631c2013-12-03 13:22:48 +000092 return val;
93 }
94
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +000095 static T InternalReadLittleEndian(const uint8_t* data) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +000096 T val(0);
sprangbe9b7b62015-09-04 01:06:57 -070097 for (unsigned int i = 0; i < B; ++i)
sprang@webrtc.org499631c2013-12-03 13:22:48 +000098 val |= static_cast<T>(data[i]) << (i * 8);
sprangbe9b7b62015-09-04 01:06:57 -070099 return val;
100 }
101};
102
103// Specialization of ByteReader for signed types.
104template <typename T, unsigned int B>
105class ByteReader<T, B, true> {
106 public:
107 typedef typename UnsignedOf<T>::Type U;
108
109 static T ReadBigEndian(const uint8_t* data) {
110 U unsigned_val = ByteReader<T, B, false>::ReadBigEndian(data);
111 if (B < sizeof(T))
112 unsigned_val = SignExtend(unsigned_val);
113 return ReinterpretAsSigned(unsigned_val);
114 }
115
116 static T ReadLittleEndian(const uint8_t* data) {
117 U unsigned_val = ByteReader<T, B, false>::ReadLittleEndian(data);
118 if (B < sizeof(T))
119 unsigned_val = SignExtend(unsigned_val);
120 return ReinterpretAsSigned(unsigned_val);
121 }
122
123 private:
124 // As a hack to avoid implementation-specific or undefined behavior when
125 // bit-shifting or casting signed integers, read as a signed equivalent
126 // instead and convert to signed. This is safe since we have asserted that
127 // two's complement for is used.
128 static T ReinterpretAsSigned(U unsigned_val) {
129 // An unsigned value with only the highest order bit set (ex 0x80).
Yves Gerey665174f2018-06-19 15:03:05 +0200130 const U kUnsignedHighestBitMask = static_cast<U>(1)
131 << ((sizeof(U) * 8) - 1);
sprangbe9b7b62015-09-04 01:06:57 -0700132 // A signed value with only the highest bit set. Since this is two's
133 // complement form, we can use the min value from std::numeric_limits.
134 const T kSignedHighestBitMask = std::numeric_limits<T>::min();
135
136 T val;
137 if ((unsigned_val & kUnsignedHighestBitMask) != 0) {
138 // Casting is only safe when unsigned value can be represented in the
139 // signed target type, so mask out highest bit and mask it back manually.
140 val = static_cast<T>(unsigned_val & ~kUnsignedHighestBitMask);
141 val |= kSignedHighestBitMask;
142 } else {
143 val = static_cast<T>(unsigned_val);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000144 }
145 return val;
146 }
147
148 // If number of bytes is less than native data type (eg 24 bit, in int32_t),
149 // and the most significant bit of the actual data is set, we must sign
150 // extend the remaining byte(s) with ones so that the correct negative
151 // number is retained.
152 // Ex: 0x810A0B -> 0xFF810A0B, but 0x710A0B -> 0x00710A0B
sprangbe9b7b62015-09-04 01:06:57 -0700153 static U SignExtend(const U val) {
154 const uint8_t kMsb = static_cast<uint8_t>(val >> ((B - 1) * 8));
155 if ((kMsb & 0x80) != 0) {
156 // Create a mask where all bits used by the B bytes are set to one,
157 // for instance 0x00FFFFFF for B = 3. Bit-wise invert that mask (to
158 // (0xFF000000 in the example above) and add it to the input value.
159 // The "B % sizeof(T)" is a workaround to undefined values warnings for
160 // B == sizeof(T), in which case this code won't be called anyway.
161 const U kUsedBitsMask = (1 << ((B % sizeof(T)) * 8)) - 1;
162 return ~kUsedBitsMask | val;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000163 }
164 return val;
165 }
166};
167
168// Class for writing integers to a sequence of bytes
169// T = type of integer, B = bytes to write
sprangbe9b7b62015-09-04 01:06:57 -0700170template <typename T,
171 unsigned int B = sizeof(T),
172 bool is_signed = std::numeric_limits<T>::is_signed>
173class ByteWriter;
174
175// Specialization of ByteWriter for unsigned types.
176template <typename T, unsigned int B>
177class ByteWriter<T, B, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000178 public:
179 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700180 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000181 for (unsigned int i = 0; i < B; ++i) {
182 data[i] = val >> ((B - 1 - i) * 8);
183 }
184 }
185
186 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700187 static_assert(B <= sizeof(T), kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000188 for (unsigned int i = 0; i < B; ++i) {
189 data[i] = val >> (i * 8);
190 }
191 }
192};
193
sprangbe9b7b62015-09-04 01:06:57 -0700194// Specialization of ByteWriter for signed types.
195template <typename T, unsigned int B>
196class ByteWriter<T, B, true> {
197 public:
198 typedef typename UnsignedOf<T>::Type U;
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000199
sprangbe9b7b62015-09-04 01:06:57 -0700200 static void WriteBigEndian(uint8_t* data, T val) {
201 ByteWriter<U, B, false>::WriteBigEndian(data, ReinterpretAsUnsigned(val));
202 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000203
sprangbe9b7b62015-09-04 01:06:57 -0700204 static void WriteLittleEndian(uint8_t* data, T val) {
205 ByteWriter<U, B, false>::WriteLittleEndian(data,
206 ReinterpretAsUnsigned(val));
207 }
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000208
sprangbe9b7b62015-09-04 01:06:57 -0700209 private:
210 static U ReinterpretAsUnsigned(T val) {
211 // According to ISO C standard ISO/IEC 9899, section 6.3.1.3 (1, 2) a
212 // conversion from signed to unsigned keeps the value if the new type can
213 // represent it, and otherwise adds one more than the max value of T until
214 // the value is in range. For two's complement, this fortunately means
215 // that the bit-wise value will be intact. Thus, since we have asserted that
216 // two's complement form is actually used, a simple cast is sufficient.
217 return static_cast<U>(val);
218 }
219};
220
221// ----- Below follows specializations of UnsignedOf utility class -----
222
223template <>
224struct UnsignedOf<int8_t> {
225 typedef uint8_t Type;
226};
227template <>
228struct UnsignedOf<int16_t> {
229 typedef uint16_t Type;
230};
231template <>
232struct UnsignedOf<int32_t> {
233 typedef uint32_t Type;
234};
235template <>
236struct UnsignedOf<int64_t> {
237 typedef uint64_t Type;
238};
239
240// ----- Below follows specializations for unsigned, B in { 1, 2, 4, 8 } -----
241
242// TODO(sprang): Check if these actually help or if generic cases will be
243// unrolled to and optimized to similar performance.
244
245// Specializations for single bytes
246template <typename T>
247class ByteReader<T, 1, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000248 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000249 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700250 static_assert(sizeof(T) == 1, kSizeErrorMsg);
251 return data[0];
252 }
253
254 static T ReadLittleEndian(const uint8_t* data) {
255 static_assert(sizeof(T) == 1, kSizeErrorMsg);
256 return data[0];
257 }
258};
259
260template <typename T>
261class ByteWriter<T, 1, false> {
262 public:
263 static void WriteBigEndian(uint8_t* data, T val) {
264 static_assert(sizeof(T) == 1, kSizeErrorMsg);
265 data[0] = val;
266 }
267
268 static void WriteLittleEndian(uint8_t* data, T val) {
269 static_assert(sizeof(T) == 1, kSizeErrorMsg);
270 data[0] = val;
271 }
272};
273
274// Specializations for two byte words
275template <typename T>
276class ByteReader<T, 2, false> {
277 public:
278 static T ReadBigEndian(const uint8_t* data) {
279 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000280 return (data[0] << 8) | data[1];
281 }
282
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000283 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700284 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000285 return data[0] | (data[1] << 8);
286 }
287};
288
sprangbe9b7b62015-09-04 01:06:57 -0700289template <typename T>
290class ByteWriter<T, 2, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000291 public:
292 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700293 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000294 data[0] = val >> 8;
295 data[1] = val;
296 }
297
298 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700299 static_assert(sizeof(T) >= 2, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000300 data[0] = val;
301 data[1] = val >> 8;
302 }
303};
304
305// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700306template <typename T>
307class ByteReader<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000308 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000309 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700310 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800311 return (Get(data, 0) << 24) | (Get(data, 1) << 16) | (Get(data, 2) << 8) |
312 Get(data, 3);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000313 }
314
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000315 static T ReadLittleEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700316 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
henrik.lundin7a839512016-01-24 23:47:51 -0800317 return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
318 (Get(data, 3) << 24);
319 }
320
321 private:
322 inline static T Get(const uint8_t* data, unsigned int index) {
323 return static_cast<T>(data[index]);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000324 }
325};
326
327// Specializations for four byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700328template <typename T>
329class ByteWriter<T, 4, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000330 public:
331 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700332 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000333 data[0] = val >> 24;
334 data[1] = val >> 16;
335 data[2] = val >> 8;
336 data[3] = val;
337 }
338
339 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700340 static_assert(sizeof(T) >= 4, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000341 data[0] = val;
342 data[1] = val >> 8;
343 data[2] = val >> 16;
344 data[3] = val >> 24;
345 }
346};
347
348// Specializations for eight byte words.
sprangbe9b7b62015-09-04 01:06:57 -0700349template <typename T>
350class ByteReader<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000351 public:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000352 static T ReadBigEndian(const uint8_t* data) {
sprangbe9b7b62015-09-04 01:06:57 -0700353 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
Yves Gerey665174f2018-06-19 15:03:05 +0200354 return (Get(data, 0) << 56) | (Get(data, 1) << 48) | (Get(data, 2) << 40) |
355 (Get(data, 3) << 32) | (Get(data, 4) << 24) | (Get(data, 5) << 16) |
356 (Get(data, 6) << 8) | Get(data, 7);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000357 }
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);
Yves Gerey665174f2018-06-19 15:03:05 +0200361 return Get(data, 0) | (Get(data, 1) << 8) | (Get(data, 2) << 16) |
362 (Get(data, 3) << 24) | (Get(data, 4) << 32) | (Get(data, 5) << 40) |
363 (Get(data, 6) << 48) | (Get(data, 7) << 56);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000364 }
365
366 private:
sprang@webrtc.org2a6558c2015-01-28 12:37:36 +0000367 inline static T Get(const uint8_t* data, unsigned int index) {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000368 return static_cast<T>(data[index]);
369 }
370};
371
sprangbe9b7b62015-09-04 01:06:57 -0700372template <typename T>
373class ByteWriter<T, 8, false> {
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000374 public:
375 static void WriteBigEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700376 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000377 data[0] = val >> 56;
378 data[1] = val >> 48;
379 data[2] = val >> 40;
380 data[3] = val >> 32;
381 data[4] = val >> 24;
382 data[5] = val >> 16;
383 data[6] = val >> 8;
384 data[7] = val;
385 }
386
387 static void WriteLittleEndian(uint8_t* data, T val) {
sprangbe9b7b62015-09-04 01:06:57 -0700388 static_assert(sizeof(T) >= 8, kSizeErrorMsg);
sprang@webrtc.org499631c2013-12-03 13:22:48 +0000389 data[0] = val;
390 data[1] = val >> 8;
391 data[2] = val >> 16;
392 data[3] = val >> 24;
393 data[4] = val >> 32;
394 data[5] = val >> 40;
395 data[6] = val >> 48;
396 data[7] = val >> 56;
397 }
398};
399
400} // namespace webrtc
401
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200402#endif // MODULES_RTP_RTCP_SOURCE_BYTE_IO_H_