Bug 1837557 - Productionize webgpu pushErrorScope/popErrorScope. r=webgpu-reviewers...
[gecko.git] / mfbt / EndianUtils.h
blobb6f3e2c315d028a268a95c39b2c46c5d229ba64b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Functions for reading and writing integers in various endiannesses. */
9 /*
10 * The classes LittleEndian and BigEndian expose static methods for
11 * reading and writing 16-, 32-, and 64-bit signed and unsigned integers
12 * in their respective endianness. The addresses read from or written
13 * to may be misaligned (although misaligned accesses may incur
14 * architecture-specific performance costs). The naming scheme is:
16 * {Little,Big}Endian::{read,write}{Uint,Int}<bitsize>
18 * For instance, LittleEndian::readInt32 will read a 32-bit signed
19 * integer from memory in little endian format. Similarly,
20 * BigEndian::writeUint16 will write a 16-bit unsigned integer to memory
21 * in big-endian format.
23 * The class NativeEndian exposes methods for conversion of existing
24 * data to and from the native endianness. These methods are intended
25 * for cases where data needs to be transferred, serialized, etc.
26 * swap{To,From}{Little,Big}Endian byteswap a single value if necessary.
27 * Bulk conversion functions are also provided which optimize the
28 * no-conversion-needed case:
30 * - copyAndSwap{To,From}{Little,Big}Endian;
31 * - swap{To,From}{Little,Big}EndianInPlace.
33 * The *From* variants are intended to be used for reading data and the
34 * *To* variants for writing data.
36 * Methods on NativeEndian work with integer data of any type.
37 * Floating-point data is not supported.
39 * For clarity in networking code, "Network" may be used as a synonym
40 * for "Big" in any of the above methods or class names.
42 * As an example, reading a file format header whose fields are stored
43 * in big-endian format might look like:
45 * class ExampleHeader
46 * {
47 * private:
48 * uint32_t mMagic;
49 * uint32_t mLength;
50 * uint32_t mTotalRecords;
51 * uint64_t mChecksum;
53 * public:
54 * ExampleHeader(const void* data)
55 * {
56 * const uint8_t* ptr = static_cast<const uint8_t*>(data);
57 * mMagic = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
58 * mLength = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
59 * mTotalRecords = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t);
60 * mChecksum = BigEndian::readUint64(ptr);
61 * }
62 * ...
63 * };
66 #ifndef mozilla_EndianUtils_h
67 #define mozilla_EndianUtils_h
69 #include "mozilla/Assertions.h"
70 #include "mozilla/Attributes.h"
71 #include "mozilla/Compiler.h"
72 #include "mozilla/DebugOnly.h"
74 #include <stdint.h>
75 #include <string.h>
77 #if defined(_MSC_VER)
78 # include <stdlib.h>
79 # pragma intrinsic(_byteswap_ushort)
80 # pragma intrinsic(_byteswap_ulong)
81 # pragma intrinsic(_byteswap_uint64)
82 #endif
85 * Our supported compilers provide architecture-independent macros for this.
86 * Yes, there are more than two values for __BYTE_ORDER__.
88 #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
89 defined(__ORDER_BIG_ENDIAN__)
90 # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
91 # define MOZ_LITTLE_ENDIAN() 1
92 # define MOZ_BIG_ENDIAN() 0
93 # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
94 # define MOZ_LITTLE_ENDIAN() 0
95 # define MOZ_BIG_ENDIAN() 1
96 # else
97 # error "Can't handle mixed-endian architectures"
98 # endif
99 #else
100 # error "Don't know how to determine endianness"
101 #endif
103 #if defined(__clang__)
104 # if __has_builtin(__builtin_bswap16)
105 # define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
106 # endif
107 #elif defined(__GNUC__)
108 # define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
109 #elif defined(_MSC_VER)
110 # define MOZ_HAVE_BUILTIN_BYTESWAP16 _byteswap_ushort
111 #endif
113 namespace mozilla {
115 namespace detail {
118 * We need wrappers here because free functions with default template
119 * arguments and/or partial specialization of function templates are not
120 * supported by all the compilers we use.
122 template <typename T, size_t Size = sizeof(T)>
123 struct Swapper;
125 template <typename T>
126 struct Swapper<T, 2> {
127 static T swap(T aValue) {
128 #if defined(MOZ_HAVE_BUILTIN_BYTESWAP16)
129 return MOZ_HAVE_BUILTIN_BYTESWAP16(aValue);
130 #else
131 return T(((aValue & 0x00ff) << 8) | ((aValue & 0xff00) >> 8));
132 #endif
136 template <typename T>
137 struct Swapper<T, 4> {
138 static T swap(T aValue) {
139 #if defined(__clang__) || defined(__GNUC__)
140 return T(__builtin_bswap32(aValue));
141 #elif defined(_MSC_VER)
142 return T(_byteswap_ulong(aValue));
143 #else
144 return T(((aValue & 0x000000ffU) << 24) | ((aValue & 0x0000ff00U) << 8) |
145 ((aValue & 0x00ff0000U) >> 8) | ((aValue & 0xff000000U) >> 24));
146 #endif
150 template <typename T>
151 struct Swapper<T, 8> {
152 static inline T swap(T aValue) {
153 #if defined(__clang__) || defined(__GNUC__)
154 return T(__builtin_bswap64(aValue));
155 #elif defined(_MSC_VER)
156 return T(_byteswap_uint64(aValue));
157 #else
158 return T(((aValue & 0x00000000000000ffULL) << 56) |
159 ((aValue & 0x000000000000ff00ULL) << 40) |
160 ((aValue & 0x0000000000ff0000ULL) << 24) |
161 ((aValue & 0x00000000ff000000ULL) << 8) |
162 ((aValue & 0x000000ff00000000ULL) >> 8) |
163 ((aValue & 0x0000ff0000000000ULL) >> 24) |
164 ((aValue & 0x00ff000000000000ULL) >> 40) |
165 ((aValue & 0xff00000000000000ULL) >> 56));
166 #endif
170 enum Endianness { Little, Big };
172 #if MOZ_BIG_ENDIAN()
173 # define MOZ_NATIVE_ENDIANNESS detail::Big
174 #else
175 # define MOZ_NATIVE_ENDIANNESS detail::Little
176 #endif
178 class EndianUtils {
180 * Assert that the memory regions [aDest, aDest+aCount) and
181 * [aSrc, aSrc+aCount] do not overlap. aCount is given in bytes.
183 static void assertNoOverlap(const void* aDest, const void* aSrc,
184 size_t aCount) {
185 DebugOnly<const uint8_t*> byteDestPtr = static_cast<const uint8_t*>(aDest);
186 DebugOnly<const uint8_t*> byteSrcPtr = static_cast<const uint8_t*>(aSrc);
187 MOZ_ASSERT(
188 (byteDestPtr <= byteSrcPtr && byteDestPtr + aCount <= byteSrcPtr) ||
189 (byteSrcPtr <= byteDestPtr && byteSrcPtr + aCount <= byteDestPtr));
192 template <typename T>
193 static void assertAligned(T* aPtr) {
194 MOZ_ASSERT((uintptr_t(aPtr) % sizeof(T)) == 0, "Unaligned pointer!");
197 protected:
199 * Return |aValue| converted from SourceEndian encoding to DestEndian
200 * encoding.
202 template <Endianness SourceEndian, Endianness DestEndian, typename T>
203 static inline T maybeSwap(T aValue) {
204 if (SourceEndian == DestEndian) {
205 return aValue;
207 return Swapper<T>::swap(aValue);
211 * Convert |aCount| elements at |aPtr| from SourceEndian encoding to
212 * DestEndian encoding.
214 template <Endianness SourceEndian, Endianness DestEndian, typename T>
215 static inline void maybeSwapInPlace(T* aPtr, size_t aCount) {
216 assertAligned(aPtr);
218 if (SourceEndian == DestEndian) {
219 return;
221 for (size_t i = 0; i < aCount; i++) {
222 aPtr[i] = Swapper<T>::swap(aPtr[i]);
227 * Write |aCount| elements to the unaligned address |aDest| in DestEndian
228 * format, using elements found at |aSrc| in SourceEndian format.
230 template <Endianness SourceEndian, Endianness DestEndian, typename T>
231 static void copyAndSwapTo(void* aDest, const T* aSrc, size_t aCount) {
232 assertNoOverlap(aDest, aSrc, aCount * sizeof(T));
233 assertAligned(aSrc);
235 if (SourceEndian == DestEndian) {
236 memcpy(aDest, aSrc, aCount * sizeof(T));
237 return;
240 uint8_t* byteDestPtr = static_cast<uint8_t*>(aDest);
241 for (size_t i = 0; i < aCount; ++i) {
242 union {
243 T mVal;
244 uint8_t mBuffer[sizeof(T)];
245 } u;
246 u.mVal = maybeSwap<SourceEndian, DestEndian>(aSrc[i]);
247 memcpy(byteDestPtr, u.mBuffer, sizeof(T));
248 byteDestPtr += sizeof(T);
253 * Write |aCount| elements to |aDest| in DestEndian format, using elements
254 * found at the unaligned address |aSrc| in SourceEndian format.
256 template <Endianness SourceEndian, Endianness DestEndian, typename T>
257 static void copyAndSwapFrom(T* aDest, const void* aSrc, size_t aCount) {
258 assertNoOverlap(aDest, aSrc, aCount * sizeof(T));
259 assertAligned(aDest);
261 if (SourceEndian == DestEndian) {
262 memcpy(aDest, aSrc, aCount * sizeof(T));
263 return;
266 const uint8_t* byteSrcPtr = static_cast<const uint8_t*>(aSrc);
267 for (size_t i = 0; i < aCount; ++i) {
268 union {
269 T mVal;
270 uint8_t mBuffer[sizeof(T)];
271 } u;
272 memcpy(u.mBuffer, byteSrcPtr, sizeof(T));
273 aDest[i] = maybeSwap<SourceEndian, DestEndian>(u.mVal);
274 byteSrcPtr += sizeof(T);
279 template <Endianness ThisEndian>
280 class Endian : private EndianUtils {
281 protected:
282 /** Read a uint16_t in ThisEndian endianness from |aPtr| and return it. */
283 [[nodiscard]] static uint16_t readUint16(const void* aPtr) {
284 return read<uint16_t>(aPtr);
287 /** Read a uint32_t in ThisEndian endianness from |aPtr| and return it. */
288 [[nodiscard]] static uint32_t readUint32(const void* aPtr) {
289 return read<uint32_t>(aPtr);
292 /** Read a uint64_t in ThisEndian endianness from |aPtr| and return it. */
293 [[nodiscard]] static uint64_t readUint64(const void* aPtr) {
294 return read<uint64_t>(aPtr);
297 /** Read a uintptr_t in ThisEndian endianness from |aPtr| and return it. */
298 [[nodiscard]] static uintptr_t readUintptr(const void* aPtr) {
299 return read<uintptr_t>(aPtr);
302 /** Read an int16_t in ThisEndian endianness from |aPtr| and return it. */
303 [[nodiscard]] static int16_t readInt16(const void* aPtr) {
304 return read<int16_t>(aPtr);
307 /** Read an int32_t in ThisEndian endianness from |aPtr| and return it. */
308 [[nodiscard]] static int32_t readInt32(const void* aPtr) {
309 return read<uint32_t>(aPtr);
312 /** Read an int64_t in ThisEndian endianness from |aPtr| and return it. */
313 [[nodiscard]] static int64_t readInt64(const void* aPtr) {
314 return read<int64_t>(aPtr);
317 /** Read an intptr_t in ThisEndian endianness from |aPtr| and return it. */
318 [[nodiscard]] static intptr_t readIntptr(const void* aPtr) {
319 return read<intptr_t>(aPtr);
322 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
323 static void writeUint16(void* aPtr, uint16_t aValue) { write(aPtr, aValue); }
325 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
326 static void writeUint32(void* aPtr, uint32_t aValue) { write(aPtr, aValue); }
328 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
329 static void writeUint64(void* aPtr, uint64_t aValue) { write(aPtr, aValue); }
331 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
332 static void writeUintptr(void* aPtr, uintptr_t aValue) {
333 write(aPtr, aValue);
336 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
337 static void writeInt16(void* aPtr, int16_t aValue) { write(aPtr, aValue); }
339 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
340 static void writeInt32(void* aPtr, int32_t aValue) { write(aPtr, aValue); }
342 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
343 static void writeInt64(void* aPtr, int64_t aValue) { write(aPtr, aValue); }
345 /** Write |aValue| to |aPtr| using ThisEndian endianness. */
346 static void writeIntptr(void* aPtr, intptr_t aValue) { write(aPtr, aValue); }
349 * Converts a value of type T to little-endian format.
351 * This function is intended for cases where you have data in your
352 * native-endian format and you need it to appear in little-endian
353 * format for transmission.
355 template <typename T>
356 [[nodiscard]] static T swapToLittleEndian(T aValue) {
357 return maybeSwap<ThisEndian, Little>(aValue);
361 * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
362 * them to little-endian format if ThisEndian is Big. |aSrc| as a typed
363 * pointer must be aligned; |aDest| need not be.
365 * As with memcpy, |aDest| and |aSrc| must not overlap.
367 template <typename T>
368 static void copyAndSwapToLittleEndian(void* aDest, const T* aSrc,
369 size_t aCount) {
370 copyAndSwapTo<ThisEndian, Little>(aDest, aSrc, aCount);
374 * Likewise, but converts values in place.
376 template <typename T>
377 static void swapToLittleEndianInPlace(T* aPtr, size_t aCount) {
378 maybeSwapInPlace<ThisEndian, Little>(aPtr, aCount);
382 * Converts a value of type T to big-endian format.
384 template <typename T>
385 [[nodiscard]] static T swapToBigEndian(T aValue) {
386 return maybeSwap<ThisEndian, Big>(aValue);
390 * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
391 * them to big-endian format if ThisEndian is Little. |aSrc| as a typed
392 * pointer must be aligned; |aDest| need not be.
394 * As with memcpy, |aDest| and |aSrc| must not overlap.
396 template <typename T>
397 static void copyAndSwapToBigEndian(void* aDest, const T* aSrc,
398 size_t aCount) {
399 copyAndSwapTo<ThisEndian, Big>(aDest, aSrc, aCount);
403 * Likewise, but converts values in place.
405 template <typename T>
406 static void swapToBigEndianInPlace(T* aPtr, size_t aCount) {
407 maybeSwapInPlace<ThisEndian, Big>(aPtr, aCount);
411 * Synonyms for the big-endian functions, for better readability
412 * in network code.
415 template <typename T>
416 [[nodiscard]] static T swapToNetworkOrder(T aValue) {
417 return swapToBigEndian(aValue);
420 template <typename T>
421 static void copyAndSwapToNetworkOrder(void* aDest, const T* aSrc,
422 size_t aCount) {
423 copyAndSwapToBigEndian(aDest, aSrc, aCount);
426 template <typename T>
427 static void swapToNetworkOrderInPlace(T* aPtr, size_t aCount) {
428 swapToBigEndianInPlace(aPtr, aCount);
432 * Converts a value of type T from little-endian format.
434 template <typename T>
435 [[nodiscard]] static T swapFromLittleEndian(T aValue) {
436 return maybeSwap<Little, ThisEndian>(aValue);
440 * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
441 * them to little-endian format if ThisEndian is Big. |aDest| as a typed
442 * pointer must be aligned; |aSrc| need not be.
444 * As with memcpy, |aDest| and |aSrc| must not overlap.
446 template <typename T>
447 static void copyAndSwapFromLittleEndian(T* aDest, const void* aSrc,
448 size_t aCount) {
449 copyAndSwapFrom<Little, ThisEndian>(aDest, aSrc, aCount);
453 * Likewise, but converts values in place.
455 template <typename T>
456 static void swapFromLittleEndianInPlace(T* aPtr, size_t aCount) {
457 maybeSwapInPlace<Little, ThisEndian>(aPtr, aCount);
461 * Converts a value of type T from big-endian format.
463 template <typename T>
464 [[nodiscard]] static T swapFromBigEndian(T aValue) {
465 return maybeSwap<Big, ThisEndian>(aValue);
469 * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
470 * them to big-endian format if ThisEndian is Little. |aDest| as a typed
471 * pointer must be aligned; |aSrc| need not be.
473 * As with memcpy, |aDest| and |aSrc| must not overlap.
475 template <typename T>
476 static void copyAndSwapFromBigEndian(T* aDest, const void* aSrc,
477 size_t aCount) {
478 copyAndSwapFrom<Big, ThisEndian>(aDest, aSrc, aCount);
482 * Likewise, but converts values in place.
484 template <typename T>
485 static void swapFromBigEndianInPlace(T* aPtr, size_t aCount) {
486 maybeSwapInPlace<Big, ThisEndian>(aPtr, aCount);
490 * Synonyms for the big-endian functions, for better readability
491 * in network code.
493 template <typename T>
494 [[nodiscard]] static T swapFromNetworkOrder(T aValue) {
495 return swapFromBigEndian(aValue);
498 template <typename T>
499 static void copyAndSwapFromNetworkOrder(T* aDest, const void* aSrc,
500 size_t aCount) {
501 copyAndSwapFromBigEndian(aDest, aSrc, aCount);
504 template <typename T>
505 static void swapFromNetworkOrderInPlace(T* aPtr, size_t aCount) {
506 swapFromBigEndianInPlace(aPtr, aCount);
509 private:
511 * Read a value of type T, encoded in endianness ThisEndian from |aPtr|.
512 * Return that value encoded in native endianness.
514 template <typename T>
515 static T read(const void* aPtr) {
516 union {
517 T mVal;
518 uint8_t mBuffer[sizeof(T)];
519 } u;
520 memcpy(u.mBuffer, aPtr, sizeof(T));
521 return maybeSwap<ThisEndian, MOZ_NATIVE_ENDIANNESS>(u.mVal);
525 * Write a value of type T, in native endianness, to |aPtr|, in ThisEndian
526 * endianness.
528 template <typename T>
529 static void write(void* aPtr, T aValue) {
530 T tmp = maybeSwap<MOZ_NATIVE_ENDIANNESS, ThisEndian>(aValue);
531 memcpy(aPtr, &tmp, sizeof(T));
534 Endian() = delete;
535 Endian(const Endian& aTther) = delete;
536 void operator=(const Endian& aOther) = delete;
539 template <Endianness ThisEndian>
540 class EndianReadWrite : public Endian<ThisEndian> {
541 private:
542 typedef Endian<ThisEndian> super;
544 public:
545 using super::readInt16;
546 using super::readInt32;
547 using super::readInt64;
548 using super::readIntptr;
549 using super::readUint16;
550 using super::readUint32;
551 using super::readUint64;
552 using super::readUintptr;
553 using super::writeInt16;
554 using super::writeInt32;
555 using super::writeInt64;
556 using super::writeIntptr;
557 using super::writeUint16;
558 using super::writeUint32;
559 using super::writeUint64;
560 using super::writeUintptr;
563 } /* namespace detail */
565 class LittleEndian final : public detail::EndianReadWrite<detail::Little> {};
567 class BigEndian final : public detail::EndianReadWrite<detail::Big> {};
569 typedef BigEndian NetworkEndian;
571 class NativeEndian final : public detail::Endian<MOZ_NATIVE_ENDIANNESS> {
572 private:
573 typedef detail::Endian<MOZ_NATIVE_ENDIANNESS> super;
575 public:
577 * These functions are intended for cases where you have data in your
578 * native-endian format and you need the data to appear in the appropriate
579 * endianness for transmission, serialization, etc.
581 using super::copyAndSwapToBigEndian;
582 using super::copyAndSwapToLittleEndian;
583 using super::copyAndSwapToNetworkOrder;
584 using super::swapToBigEndian;
585 using super::swapToBigEndianInPlace;
586 using super::swapToLittleEndian;
587 using super::swapToLittleEndianInPlace;
588 using super::swapToNetworkOrder;
589 using super::swapToNetworkOrderInPlace;
592 * These functions are intended for cases where you have data in the
593 * given endianness (e.g. reading from disk or a file-format) and you
594 * need the data to appear in native-endian format for processing.
596 using super::copyAndSwapFromBigEndian;
597 using super::copyAndSwapFromLittleEndian;
598 using super::copyAndSwapFromNetworkOrder;
599 using super::swapFromBigEndian;
600 using super::swapFromBigEndianInPlace;
601 using super::swapFromLittleEndian;
602 using super::swapFromLittleEndianInPlace;
603 using super::swapFromNetworkOrder;
604 using super::swapFromNetworkOrderInPlace;
607 #undef MOZ_NATIVE_ENDIANNESS
609 } /* namespace mozilla */
611 #endif /* mozilla_EndianUtils_h */