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. */
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:
50 * uint32_t mTotalRecords;
54 * ExampleHeader(const void* data)
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);
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"
79 # pragma intrinsic(_byteswap_ushort)
80 # pragma intrinsic(_byteswap_ulong)
81 # pragma intrinsic(_byteswap_uint64)
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
97 # error "Can't handle mixed-endian architectures"
100 # error "Don't know how to determine endianness"
103 #if defined(__clang__)
104 # if __has_builtin(__builtin_bswap16)
105 # define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
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
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
)>
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
);
131 return T(((aValue
& 0x00ff) << 8) | ((aValue
& 0xff00) >> 8));
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
));
144 return T(((aValue
& 0x000000ffU
) << 24) | ((aValue
& 0x0000ff00U
) << 8) |
145 ((aValue
& 0x00ff0000U
) >> 8) | ((aValue
& 0xff000000U
) >> 24));
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
));
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));
170 enum Endianness
{ Little
, Big
};
173 # define MOZ_NATIVE_ENDIANNESS detail::Big
175 # define MOZ_NATIVE_ENDIANNESS detail::Little
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
,
185 DebugOnly
<const uint8_t*> byteDestPtr
= static_cast<const uint8_t*>(aDest
);
186 DebugOnly
<const uint8_t*> byteSrcPtr
= static_cast<const uint8_t*>(aSrc
);
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!");
199 * Return |aValue| converted from SourceEndian encoding to DestEndian
202 template <Endianness SourceEndian
, Endianness DestEndian
, typename T
>
203 static inline T
maybeSwap(T aValue
) {
204 if (SourceEndian
== DestEndian
) {
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
) {
218 if (SourceEndian
== DestEndian
) {
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
));
235 if (SourceEndian
== DestEndian
) {
236 memcpy(aDest
, aSrc
, aCount
* sizeof(T
));
240 uint8_t* byteDestPtr
= static_cast<uint8_t*>(aDest
);
241 for (size_t i
= 0; i
< aCount
; ++i
) {
244 uint8_t mBuffer
[sizeof(T
)];
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
));
266 const uint8_t* byteSrcPtr
= static_cast<const uint8_t*>(aSrc
);
267 for (size_t i
= 0; i
< aCount
; ++i
) {
270 uint8_t mBuffer
[sizeof(T
)];
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
{
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
) {
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
,
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
,
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
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
,
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
,
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
,
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
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
,
501 copyAndSwapFromBigEndian(aDest
, aSrc
, aCount
);
504 template <typename T
>
505 static void swapFromNetworkOrderInPlace(T
* aPtr
, size_t aCount
) {
506 swapFromBigEndianInPlace(aPtr
, aCount
);
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
) {
518 uint8_t mBuffer
[sizeof(T
)];
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
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
));
535 Endian(const Endian
& aTther
) = delete;
536 void operator=(const Endian
& aOther
) = delete;
539 template <Endianness ThisEndian
>
540 class EndianReadWrite
: public Endian
<ThisEndian
> {
542 typedef Endian
<ThisEndian
> super
;
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
> {
573 typedef detail::Endian
<MOZ_NATIVE_ENDIANNESS
> super
;
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 */