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/. */ 6 7 /* Functions for reading and writing integers in various endiannesses. */ 8 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: 15 * 16 * {Little,Big}Endian::{read,write}{Uint,Int}<bitsize> 17 * 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. 22 * 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: 29 * 30 * - copyAndSwap{To,From}{Little,Big}Endian; 31 * - swap{To,From}{Little,Big}EndianInPlace. 32 * 33 * The *From* variants are intended to be used for reading data and the 34 * *To* variants for writing data. 35 * 36 * Methods on NativeEndian work with integer data of any type. 37 * Floating-point data is not supported. 38 * 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. 41 * 42 * As an example, reading a file format header whose fields are stored 43 * in big-endian format might look like: 44 * 45 * class ExampleHeader 46 * { 47 * private: 48 * uint32_t mMagic; 49 * uint32_t mLength; 50 * uint32_t mTotalRecords; 51 * uint64_t mChecksum; 52 * 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 * }; 64 */ 65 66 #ifndef mozilla_EndianUtils_h 67 #define mozilla_EndianUtils_h 68 69 #include "mozilla/Assertions.h" 70 #include "mozilla/Attributes.h" 71 #include "mozilla/Compiler.h" 72 #include "mozilla/DebugOnly.h" 73 74 #include <stdint.h> 75 #include <string.h> 76 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 83 84 /* 85 * Our supported compilers provide architecture-independent macros for this. 86 * Yes, there are more than two values for __BYTE_ORDER__. 87 */ 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 102 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 112 113 namespace mozilla { 114 115 namespace detail { 116 117 /* 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. 121 */ 122 template <typename T, size_t Size = sizeof(T)> 123 struct Swapper; 124 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 133 } 134 }; 135 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 147 } 148 }; 149 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 167 } 168 }; 169 170 enum Endianness { Little, Big }; 171 172 #if MOZ_BIG_ENDIAN() 173 # define MOZ_NATIVE_ENDIANNESS detail::Big 174 #else 175 # define MOZ_NATIVE_ENDIANNESS detail::Little 176 #endif 177 178 class EndianUtils { 179 /** 180 * Assert that the memory regions [aDest, aDest+aCount) and 181 * [aSrc, aSrc+aCount] do not overlap. aCount is given in bytes. 182 */ 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)); 190 } 191 192 template <typename T> 193 static void assertAligned(T* aPtr) { 194 MOZ_ASSERT((uintptr_t(aPtr) % sizeof(T)) == 0, "Unaligned pointer!"); 195 } 196 197 protected: 198 /** 199 * Return |aValue| converted from SourceEndian encoding to DestEndian 200 * encoding. 201 */ 202 template <Endianness SourceEndian, Endianness DestEndian, typename T> 203 static inline T maybeSwap(T aValue) { 204 if (SourceEndian == DestEndian) { 205 return aValue; 206 } 207 return Swapper<T>::swap(aValue); 208 } 209 210 /** 211 * Convert |aCount| elements at |aPtr| from SourceEndian encoding to 212 * DestEndian encoding. 213 */ 214 template <Endianness SourceEndian, Endianness DestEndian, typename T> 215 static inline void maybeSwapInPlace(T* aPtr, size_t aCount) { 216 assertAligned(aPtr); 217 218 if (SourceEndian == DestEndian) { 219 return; 220 } 221 for (size_t i = 0; i < aCount; i++) { 222 aPtr[i] = Swapper<T>::swap(aPtr[i]); 223 } 224 } 225 226 /** 227 * Write |aCount| elements to the unaligned address |aDest| in DestEndian 228 * format, using elements found at |aSrc| in SourceEndian format. 229 */ 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); 234 235 if (SourceEndian == DestEndian) { 236 memcpy(aDest, aSrc, aCount * sizeof(T)); 237 return; 238 } 239 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); 249 } 250 } 251 252 /** 253 * Write |aCount| elements to |aDest| in DestEndian format, using elements 254 * found at the unaligned address |aSrc| in SourceEndian format. 255 */ 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); 260 261 if (SourceEndian == DestEndian) { 262 memcpy(aDest, aSrc, aCount * sizeof(T)); 263 return; 264 } 265 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); 275 } 276 } 277 }; 278 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); 285 } 286 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); 290 } 291 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); 295 } 296 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); 300 } 301 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); 305 } 306 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); 310 } 311 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); 315 } 316 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); 320 } 321 322 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 323 static void writeUint16(void* aPtr, uint16_t aValue) { write(aPtr, aValue); } 324 325 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 326 static void writeUint32(void* aPtr, uint32_t aValue) { write(aPtr, aValue); } 327 328 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 329 static void writeUint64(void* aPtr, uint64_t aValue) { write(aPtr, aValue); } 330 331 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 332 static void writeUintptr(void* aPtr, uintptr_t aValue) { 333 write(aPtr, aValue); 334 } 335 336 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 337 static void writeInt16(void* aPtr, int16_t aValue) { write(aPtr, aValue); } 338 339 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 340 static void writeInt32(void* aPtr, int32_t aValue) { write(aPtr, aValue); } 341 342 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 343 static void writeInt64(void* aPtr, int64_t aValue) { write(aPtr, aValue); } 344 345 /** Write |aValue| to |aPtr| using ThisEndian endianness. */ 346 static void writeIntptr(void* aPtr, intptr_t aValue) { write(aPtr, aValue); } 347 348 /* 349 * Converts a value of type T to little-endian format. 350 * 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. 354 */ 355 template <typename T> 356 [[nodiscard]] static T swapToLittleEndian(T aValue) { 357 return maybeSwap<ThisEndian, Little>(aValue); 358 } 359 360 /* 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. 364 * 365 * As with memcpy, |aDest| and |aSrc| must not overlap. 366 */ 367 template <typename T> 368 static void copyAndSwapToLittleEndian(void* aDest, const T* aSrc, 369 size_t aCount) { 370 copyAndSwapTo<ThisEndian, Little>(aDest, aSrc, aCount); 371 } 372 373 /* 374 * Likewise, but converts values in place. 375 */ 376 template <typename T> 377 static void swapToLittleEndianInPlace(T* aPtr, size_t aCount) { 378 maybeSwapInPlace<ThisEndian, Little>(aPtr, aCount); 379 } 380 381 /* 382 * Converts a value of type T to big-endian format. 383 */ 384 template <typename T> 385 [[nodiscard]] static T swapToBigEndian(T aValue) { 386 return maybeSwap<ThisEndian, Big>(aValue); 387 } 388 389 /* 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. 393 * 394 * As with memcpy, |aDest| and |aSrc| must not overlap. 395 */ 396 template <typename T> 397 static void copyAndSwapToBigEndian(void* aDest, const T* aSrc, 398 size_t aCount) { 399 copyAndSwapTo<ThisEndian, Big>(aDest, aSrc, aCount); 400 } 401 402 /* 403 * Likewise, but converts values in place. 404 */ 405 template <typename T> 406 static void swapToBigEndianInPlace(T* aPtr, size_t aCount) { 407 maybeSwapInPlace<ThisEndian, Big>(aPtr, aCount); 408 } 409 410 /* 411 * Synonyms for the big-endian functions, for better readability 412 * in network code. 413 */ 414 415 template <typename T> 416 [[nodiscard]] static T swapToNetworkOrder(T aValue) { 417 return swapToBigEndian(aValue); 418 } 419 420 template <typename T> 421 static void copyAndSwapToNetworkOrder(void* aDest, const T* aSrc, 422 size_t aCount) { 423 copyAndSwapToBigEndian(aDest, aSrc, aCount); 424 } 425 426 template <typename T> 427 static void swapToNetworkOrderInPlace(T* aPtr, size_t aCount) { 428 swapToBigEndianInPlace(aPtr, aCount); 429 } 430 431 /* 432 * Converts a value of type T from little-endian format. 433 */ 434 template <typename T> 435 [[nodiscard]] static T swapFromLittleEndian(T aValue) { 436 return maybeSwap<Little, ThisEndian>(aValue); 437 } 438 439 /* 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. 443 * 444 * As with memcpy, |aDest| and |aSrc| must not overlap. 445 */ 446 template <typename T> 447 static void copyAndSwapFromLittleEndian(T* aDest, const void* aSrc, 448 size_t aCount) { 449 copyAndSwapFrom<Little, ThisEndian>(aDest, aSrc, aCount); 450 } 451 452 /* 453 * Likewise, but converts values in place. 454 */ 455 template <typename T> 456 static void swapFromLittleEndianInPlace(T* aPtr, size_t aCount) { 457 maybeSwapInPlace<Little, ThisEndian>(aPtr, aCount); 458 } 459 460 /* 461 * Converts a value of type T from big-endian format. 462 */ 463 template <typename T> 464 [[nodiscard]] static T swapFromBigEndian(T aValue) { 465 return maybeSwap<Big, ThisEndian>(aValue); 466 } 467 468 /* 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. 472 * 473 * As with memcpy, |aDest| and |aSrc| must not overlap. 474 */ 475 template <typename T> 476 static void copyAndSwapFromBigEndian(T* aDest, const void* aSrc, 477 size_t aCount) { 478 copyAndSwapFrom<Big, ThisEndian>(aDest, aSrc, aCount); 479 } 480 481 /* 482 * Likewise, but converts values in place. 483 */ 484 template <typename T> 485 static void swapFromBigEndianInPlace(T* aPtr, size_t aCount) { 486 maybeSwapInPlace<Big, ThisEndian>(aPtr, aCount); 487 } 488 489 /* 490 * Synonyms for the big-endian functions, for better readability 491 * in network code. 492 */ 493 template <typename T> 494 [[nodiscard]] static T swapFromNetworkOrder(T aValue) { 495 return swapFromBigEndian(aValue); 496 } 497 498 template <typename T> 499 static void copyAndSwapFromNetworkOrder(T* aDest, const void* aSrc, 500 size_t aCount) { 501 copyAndSwapFromBigEndian(aDest, aSrc, aCount); 502 } 503 504 template <typename T> 505 static void swapFromNetworkOrderInPlace(T* aPtr, size_t aCount) { 506 swapFromBigEndianInPlace(aPtr, aCount); 507 } 508 509 private: 510 /** 511 * Read a value of type T, encoded in endianness ThisEndian from |aPtr|. 512 * Return that value encoded in native endianness. 513 */ 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); 522 } 523 524 /** 525 * Write a value of type T, in native endianness, to |aPtr|, in ThisEndian 526 * endianness. 527 */ 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)); 532 } 533 534 Endian() = delete; 535 Endian(const Endian& aTther) = delete; 536 void operator=(const Endian& aOther) = delete; 537 }; 538 539 template <Endianness ThisEndian> 540 class EndianReadWrite : public Endian<ThisEndian> { 541 private: 542 typedef Endian<ThisEndian> super; 543 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; 561 }; 562 563 } /* namespace detail */ 564 565 class LittleEndian final : public detail::EndianReadWrite<detail::Little> {}; 566 567 class BigEndian final : public detail::EndianReadWrite<detail::Big> {}; 568 569 typedef BigEndian NetworkEndian; 570 571 class NativeEndian final : public detail::Endian<MOZ_NATIVE_ENDIANNESS> { 572 private: 573 typedef detail::Endian<MOZ_NATIVE_ENDIANNESS> super; 574 575 public: 576 /* 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. 580 */ 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; 590 591 /* 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. 595 */ 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; 605 }; 606 607 #undef MOZ_NATIVE_ENDIANNESS 608 609 } /* namespace mozilla */ 610 611 #endif /* mozilla_EndianUtils_h */ 612