├── .gitignore ├── README.md ├── endian.hpp └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | test/a.out 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Endian 2 | 3 | A small, but much needed utility library for any program that needs to handle numbers during network or file IO. 4 | 5 | ## Usage 6 | 7 | This is a header-only library, so just place `endian.hpp` in your system wide or project's include directory. Then, use the library by including endian, i.e.: 8 | ```c++ 9 | #include 10 | ``` 11 | 12 | ### Working with buffers 13 | 14 | Convert from Network Byte Order to Host Byte Order, e.g. when using a receive buffer. 15 | ```c++ 16 | std::vector buffer; 17 | // ... receive data into buffer 18 | const int64_t i = endian::read(buffer.data()); 19 | ``` 20 | 21 | Convert from Host Byte Order to Network Byte Order, e.g. when using a send buffer. 22 | ```c++ 23 | std::vector buffer; 24 | const int16_t number = 42; 25 | endian::write(number, buffer.data()); 26 | ``` 27 | 28 | You can also read and parse arbitrary width integers in the range [1, 8] specified in bytes . 29 | ```c++ 30 | const uint32_t three_bytes = 0xaabbcc; 31 | endian::write(number, buffer.data()); 32 | auto res = endian::read(buffer.data()); 33 | assert(res = three_bytes); 34 | ``` 35 | 36 | There are also aliases provided: 37 | ```c++ 38 | endian::write_le<3>(number, buffer.data()); 39 | endian::write_be<3>(number, buffer.data()); 40 | endian::write_le(number, buffer.data()); 41 | endian::write_be(number, buffer.data()); 42 | 43 | number = endian::read_le<5>(buffer.data()); 44 | number = endian::read_be<5>(buffer.data()); 45 | number = endian::read_le(buffer.data()); 46 | number = endian::read_be(buffer.data()); 47 | ``` 48 | 49 | ### Platform specific functions 50 | 51 | Note that these functions are only available if you're on one of the supported 52 | platforms (where host byte order could be determined). As such, the following 53 | functions are conditionally enabled with a preprocessor flag. 54 | 55 | #### Reversing byte order 56 | 57 | ```c++ 58 | const int16_t a = 0x1234; 59 | const int16_t b = endian::reverse(a); 60 | // b is 0x3412 61 | ``` 62 | 63 | Alternatively, only reverse byte order if target endianness and host's endianness differ. 64 | ```c++ 65 | const auto i = endian::conditional_reverse(42); 66 | ``` 67 | 68 | #### ntoh, hton 69 | 70 | Convenience function to conditionally convert to Network Byte Order, same as the POSIX hton function. 71 | ```c++ 72 | const auto n = endian::host_to_network(h); 73 | ``` 74 | 75 | Convenience function to conditionally convert from Network Byte Order, same as the POSIX ntoh function. 76 | ```c++ 77 | const auto h = endian::network_to_host(n); 78 | ``` 79 | -------------------------------------------------------------------------------- /endian.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MND_ENDIAN_HEADER 2 | #define MND_ENDIAN_HEADER 3 | 4 | // -- architecture 5 | 6 | #include // for __GLIBC__ 7 | 8 | // C++11 and below don't support contexpr as used here. 9 | #if __cplusplus >= 201300 10 | # define MND_CONSTEXPR constexpr 11 | #else 12 | # define MND_CONSTEXPR 13 | #endif 14 | 15 | // Ported from boost/predef/other/endian.h. 16 | 17 | // Copyright (C) 2012 David Stone 18 | // Copyright Beman Dawes 2013 19 | 20 | // Distributed under the Boost Software License, Version 1.0. 21 | // http://www.boost.org/LICENSE_1_0.txt 22 | #if defined(__GLIBC__) \ 23 | || defined(__GLIBCXX__) \ 24 | || defined(__GNU_LIBRARY__) \ 25 | || defined(__ANDROID__) 26 | # include 27 | #else 28 | # if defined(macintosh) \ 29 | || defined(Macintosh) \ 30 | || (defined(__APPLE__) && defined(__MACH__)) 31 | # include 32 | # elif defined(BSD) || defined(_SYSTYPE_BSD) 33 | # if defined(__OpenBSD__) 34 | # include 35 | # else 36 | # include 37 | # endif 38 | # endif 39 | #endif 40 | 41 | #if defined(__BYTE_ORDER) 42 | # if defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) 43 | # define MND_BIG_ENDIAN 44 | # elif defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) 45 | # define MND_LITTLE_ENDIAN 46 | # else 47 | # define MND_UNKNOWN_ENDIANNESS 48 | # endif 49 | #elif defined(_BYTE_ORDER) 50 | # if defined(_BIG_ENDIAN) && (_BYTE_ORDER == _BIG_ENDIAN) 51 | # define MND_BIG_ENDIAN 52 | # elif defined(_LITTLE_ENDIAN) && (_BYTE_ORDER == _LITTLE_ENDIAN) 53 | # define MND_LITTLE_ENDIAN 54 | # else 55 | # define MND_UNKNOWN_ENDIANNESS 56 | # endif 57 | #else 58 | # define MND_UNKNOWN_ENDIANNESS 59 | #endif 60 | 61 | #ifndef __has_builtin 62 | #define __has_builtin(x) 0 // Compatibility with non-clang compilers. 63 | #endif 64 | 65 | #if defined(_MSC_VER) 66 | // Microsoft documents these as being compatible since Windows 95 and 67 | // specifically lists runtime library support since Visual Studio 2003 (aka 68 | // 7.1). Clang/c2 uses the Microsoft rather than GCC intrinsics, so we check 69 | // for defined(_MSC_VER) before defined(__clang__). 70 | # include 71 | # define MND_BYTE_SWAP_16(x) _byteswap_ushort(x) 72 | # define MND_BYTE_SWAP_32(x) _byteswap_ulong(x) 73 | # define MND_BYTE_SWAP_64(x) _byteswap_uint64(x) 74 | 75 | // GCC and Clang recent versions provide intrinsic byte swaps via builtins. 76 | #elif (defined(__clang__) \ 77 | && __has_builtin(__builtin_bswap32) \ 78 | && __has_builtin(__builtin_bswap64)) \ 79 | || (defined(__GNUC__ ) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) 80 | // Prior to 4.8, gcc did not provide __builtin_bswap16 on some platforms so we emulate it 81 | // see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624. 82 | // Clang has a similar problem, but their feature test macros make it easier to detect. 83 | # if (defined(__clang__) && __has_builtin(__builtin_bswap16)) \ 84 | || (defined(__GNUC__) &&(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) 85 | # define MND_BYTE_SWAP_16(x) __builtin_bswap16(x) 86 | # else 87 | # define MND_BYTE_SWAP_16(x) __builtin_bswap32((x) << 16) 88 | # endif 89 | # define MND_BYTE_SWAP_32(x) __builtin_bswap32(x) 90 | # define MND_BYTE_SWAP_64(x) __builtin_bswap64(x) 91 | 92 | // Linux systems provide the byteswap.h header. 93 | #elif defined(__linux__) 94 | # include 95 | # define MND_BYTE_SWAP_16(x) bswap_16(x) 96 | # define MND_BYTE_SWAP_32(x) bswap_32(x) 97 | # define MND_BYTE_SWAP_64(x) bswap_64(x) 98 | 99 | // We need to provide our own implementation. 100 | #else 101 | namespace endian { 102 | namespace detail { 103 | 104 | constexpr uint16_t swap_u16(uint16_t n) noexcept 105 | { 106 | return (n << 8) | (n >> 8); 107 | } 108 | 109 | constexpr uint32_t swap_u32(uint32_t n) noexcept 110 | { 111 | n = ((n << 8) & 0xFF00FF00) | ((n >> 8) & 0xFF00FF); 112 | return (n << 16) | (n >> 16); 113 | } 114 | 115 | constexpr uint64_t swap_u64(uint64_t n) noexcept 116 | { 117 | n = ((n << 8) & 0xFF00FF00FF00FF00ull) | ((n >> 8) & 0x00FF00FF00FF00FFull); 118 | n = ((n << 16) & 0xFFFF0000FFFF0000ull) | ((n >> 16) & 0x0000FFFF0000FFFFull); 119 | return (n << 32) | (n >> 32); 120 | } 121 | 122 | } // detail 123 | } // endian 124 | # define MND_BYTE_SWAP_16(x) endian::detail::swap_u16(static_cast(x)) 125 | # define MND_BYTE_SWAP_32(x) endian::detail::swap_u32(static_cast(x)) 126 | # define MND_BYTE_SWAP_64(x) endian::detail::swap_u64(static_cast(x)) 127 | #endif 128 | 129 | // -- type traits 130 | 131 | #include 132 | #include 133 | 134 | namespace endian { 135 | namespace detail { 136 | 137 | template 138 | struct is_endian_reversible 139 | { 140 | // TODO: is is_pod right? 141 | static constexpr bool value = std::is_integral::value || std::is_pod::value; 142 | }; 143 | 144 | template 145 | struct make_void { using type = void; }; 146 | 147 | template 148 | using void_t = typename make_void::type; 149 | 150 | template 151 | struct is_input_iterator : std::false_type {}; 152 | 153 | template 154 | struct is_input_iterator()), 156 | decltype(++std::declval()), 157 | decltype(std::declval()++)>> 158 | : std::true_type {}; 159 | 160 | template 161 | struct integral_type_for 162 | { 163 | static_assert(N <= 8, "N may be at most 8 bytes large"); 164 | using type = typename std::conditional< 165 | (N > sizeof(uint8_t)), 166 | typename std::conditional< 167 | (N > sizeof(uint16_t)), 168 | typename std::conditional< 169 | (N > sizeof(uint32_t)), 170 | uint64_t, 171 | uint32_t 172 | >::type, 173 | uint16_t 174 | >::type, 175 | uint8_t 176 | >::type; 177 | }; 178 | 179 | } // detail 180 | } // endian 181 | 182 | // -- API 183 | 184 | namespace endian { 185 | 186 | enum order { 187 | little, 188 | big, 189 | network = big, 190 | #if defined(MND_BIG_ENDIAN) 191 | host = big, 192 | #elif defined(MND_LITTLE_ENDIAN) 193 | host = little, 194 | #endif 195 | }; 196 | 197 | /** 198 | * Parses `sizeof(T)` bytes from the memory pointed to by `it`, and reconstructs from it 199 | * an integer of type `T`, converting from the specified `Order` to host byte order. 200 | * 201 | * The value type of the iterator must represent a byte, that is: 202 | * `sizeof(typename std::iterator_traits::value_type) == sizeof(char)`. 203 | * 204 | * It's undefined behaviour if `it` points to a buffer smaller than `sizeof(T)` bytes. 205 | * 206 | * The byte sequence must have at least `sizeof(T)` bytes. 207 | * `Order` must be either `endian::order::big`, `endian::order::little`, 208 | * `endian::order::network`, or `endian::order::host`. 209 | * 210 | * This is best used when data received during IO is read into a buffer and numbers 211 | * need to be read from it. E.g.: 212 | * ``` 213 | * std::array buffer; 214 | * // Receive into `buffer`. 215 | * // ... 216 | * // Assume that the first four bytes in `buffer` constitute a 32-bit big endian 217 | * // integer. 218 | * int32_t n = endian::read(buffer.data()); 219 | * ``` 220 | */ 221 | template 222 | MND_CONSTEXPR T read(InputIt it) noexcept; 223 | 224 | template 225 | MND_CONSTEXPR T read_le(InputIt it) noexcept 226 | { 227 | return read(it); 228 | } 229 | 230 | template 231 | MND_CONSTEXPR T read_be(InputIt it) noexcept 232 | { 233 | return read(it); 234 | } 235 | 236 | /** 237 | * Parses `N` bytes from the memory pointed to by `it`, and reconstructs from it 238 | * an unsigned integer of type `T` that is at least as large to fit `N` bytes 239 | * (but at most 8 bytes), converting from the specified `Order` to host byte 240 | * order. 241 | * 242 | * The value type of the iterator must represent a byte, that is: 243 | * `sizeof(typename std::iterator_traits::value_type) == sizeof(char)`. 244 | * 245 | * It's undefined behaviour if `it` points to a buffer smaller than `sizeof(T)` bytes. 246 | * 247 | * The byte sequence must have at least `sizeof(T)` bytes. 248 | * `Order` must be either `endian::order::big`, `endian::order::little`, 249 | * `endian::order::network`, or `endian::order::host`. 250 | * 251 | * This is best used when data received during IO is read into a buffer and numbers 252 | * need to be read from it. E.g.: 253 | * ``` 254 | * std::array buffer; 255 | * // Receive into `buffer`. 256 | * // ... 257 | * // Assume that the first three bytes in `buffer` constitute a 24-bit big endian 258 | * // integer. 259 | * int32_t n = endian::read(buffer.data()); 260 | * ``` 261 | */ 262 | template::type> 264 | MND_CONSTEXPR T read(InputIt it) noexcept; 265 | 266 | template::type> 268 | MND_CONSTEXPR T read_le(InputIt it) noexcept 269 | { 270 | return read(it); 271 | } 272 | 273 | template::type> 275 | MND_CONSTEXPR T read_be(InputIt it) noexcept 276 | { 277 | return read(it); 278 | } 279 | 280 | // DEPRECATED: use `read` instead. 281 | template 282 | MND_CONSTEXPR T parse(InputIt it) noexcept { return read(it); } 283 | 284 | /** 285 | * Writes each byte of `h` to the memory pointed to by `it`, such that it converts the 286 | * byte order of `h` from host byte order to the specified `Order`. 287 | * 288 | * The value type of the iterator must represent a byte, that is: 289 | * `sizeof(typename std::iterator_traits::value_type) == sizeof(char)`. 290 | * 291 | * It's undefined behaviour if `it` points to a buffer smaller than `sizeof(T)` bytes. 292 | * 293 | * The byte sequence must have at least `sizeof(T)` bytes. 294 | * `Order` must be either `endian::order::big`, `endian::order::little`, 295 | * `endian::order::network`, or `endian::order::host`. 296 | * 297 | * This is best used when data transferred during IO is written to a buffer first, and 298 | * among the data to be written are integers. E.g.: 299 | * ``` 300 | * std::array buffer; 301 | * const int32_t number = 42; 302 | * // Write `number` as a big endian number to `buffer`. 303 | * endian::write(number, &buffer[0]); 304 | * // Write `number` as a little endian number to `buffer`. 305 | * endian::write(number, &buffer[4]); 306 | * ``` 307 | */ 308 | template 309 | MND_CONSTEXPR void write(const T& h, OutputIt it) noexcept; 310 | 311 | template 312 | MND_CONSTEXPR void write_le(const T& h, OutputIt it) noexcept 313 | { 314 | write(h, it); 315 | } 316 | 317 | template 318 | MND_CONSTEXPR void write_be(const T& h, OutputIt it) noexcept 319 | { 320 | write(h, it); 321 | } 322 | 323 | /** 324 | * Writes the first `N` byte of `h` to the memory pointed to by `it`, such that it converts the 325 | * byte order of `h` from host byte order to the specified `Order`. Thus `N` 326 | * must be at most `sizeof(h)`. 327 | * 328 | * The value type of the iterator must represent a byte, that is: 329 | * `sizeof(typename std::iterator_traits::value_type) == sizeof(char)`. 330 | * 331 | * It's undefined behaviour if `it` points to a buffer smaller than `N` bytes. 332 | * 333 | * The byte sequence must have at least `N` bytes. 334 | * `Order` must be either `endian::order::big`, `endian::order::little`, 335 | * `endian::order::network`, or `endian::order::host`. 336 | * 337 | * This is best used when data transferred during IO is written to a buffer first, and 338 | * among the data to be written are integers. E.g.: 339 | * ``` 340 | * std::array buffer; 341 | * const int32_t number = 42; 342 | * // Write a three byte `number` as a big endian number to `buffer`. 343 | * endian::write(number, &buffer[0]); 344 | * // Write a three byte `number` as a little endian number to `buffer`. 345 | * endian::write(number, &buffer[4]); 346 | * ``` 347 | */ 348 | template 349 | MND_CONSTEXPR void write(const T& h, OutputIt it) noexcept; 350 | 351 | template 352 | MND_CONSTEXPR void write_le(const T& h, OutputIt it) noexcept 353 | { 354 | write(h, it); 355 | } 356 | 357 | template 358 | MND_CONSTEXPR void write_be(const T& h, OutputIt it) noexcept 359 | { 360 | write(h, it); 361 | } 362 | 363 | /** 364 | * Reverses endianness, i.e. the byte order in `t`. E.g. given the 16-bit number 365 | * '0x1234', this function returns '0x4321'. 366 | */ 367 | template 368 | MND_CONSTEXPR T reverse(const T& t); 369 | 370 | #ifndef MND_UNKNOWN_ENDIANNESS 371 | /// These are only available if your platform has a defined endianness. 372 | /** 373 | * Conditionally converts to the specified endianness if and only if the host's byte 374 | * order differs from `Order`. 375 | */ 376 | template 377 | MND_CONSTEXPR T conditional_convert(const T& t) noexcept; 378 | 379 | /** 380 | * Conditionally converts to network byte order if and only if the host's byte order is 381 | * different from the network byte order. 382 | * 383 | * Functionally equivalent to the POSIX ntoh* functions or to this code: 384 | * ``` 385 | * if(endian::order::host != endian::order::network) 386 | * t = endian::reverse(t); 387 | * ``` 388 | * Or: 389 | * ``` 390 | * t = endian::conditional_convert(t); 391 | * ``` 392 | */ 393 | template 394 | MND_CONSTEXPR T host_to_network(const T& t); 395 | 396 | /** 397 | * Conditionally converts to host byte order if and only if the host's byte order is 398 | * different from the network byte order. 399 | * 400 | * Functionally equivalent to the POSIX ntoh* functions or to this code: 401 | * ``` 402 | * if(endian::order::host != endian::order::network) 403 | * t = endian::reverse(t); 404 | * ``` 405 | * Or: 406 | * ``` 407 | * t = endian::conditional_convert(t); 408 | * ``` 409 | */ 410 | template 411 | MND_CONSTEXPR T network_to_host(const T& t); 412 | #endif // MND_UNKNOWN_ENDIANNESS 413 | 414 | } // endian 415 | 416 | // -- implementation 417 | 418 | namespace endian { 419 | namespace detail { 420 | 421 | /** Reads an integer of type `T` from buffer pointed to by `it` and converts it 422 | * from BIG endian order. 423 | */ 424 | template 425 | MND_CONSTEXPR typename std::enable_if::type 426 | read(InputIt it) noexcept 427 | { 428 | static_assert(sizeof(T) >= MaxNBytes, "Can only read at most sizeof(T) bytes"); 429 | T h = 0; 430 | for(int i = 0; i < int(MaxNBytes); ++i) 431 | { 432 | h <<= 8; 433 | h |= static_cast(*it++); 434 | } 435 | return h; 436 | } 437 | 438 | /** 439 | * Reads an integer of type `T` from buffer pointed to by `it` and converts it 440 | * from LITTLE endian order. 441 | */ 442 | template 443 | MND_CONSTEXPR typename std::enable_if::type 444 | read(InputIt it) noexcept 445 | { 446 | static_assert(sizeof(T) >= MaxNBytes, "Can only read at most sizeof(T) bytes"); 447 | T h = 0; 448 | for(int i = 0; i < int(MaxNBytes); ++i) 449 | { 450 | h |= static_cast(*it++) << i * 8; 451 | } 452 | return h; 453 | } 454 | 455 | // -- 456 | 457 | /** Converts `h` to BIG endian order, writing it to buffer pointed to by `it`. */ 458 | template 459 | MND_CONSTEXPR typename std::enable_if::type 460 | write(const T& h, OutputIt it) noexcept 461 | { 462 | static_assert(sizeof(T) >= MaxNBytes, "Can only write at most sizeof(T) bytes"); 463 | for(int shift = 8 * (int(MaxNBytes) - 1); shift >= 0; shift -= 8) 464 | { 465 | *it++ = static_cast((h >> shift) & 0xff); 466 | } 467 | } 468 | 469 | /** Converts `h` to LITTLE endian order, writing it to buffer pointed to by `it`. */ 470 | template 471 | MND_CONSTEXPR typename std::enable_if::type 472 | write(const T& h, OutputIt it) noexcept 473 | { 474 | static_assert(sizeof(T) >= MaxNBytes, "Can only write at most sizeof(T) bytes"); 475 | for(int i = 0; i < int(MaxNBytes); ++i) 476 | { 477 | *it++ = static_cast((h >> i * 8) & 0xff); 478 | } 479 | } 480 | 481 | // -- 482 | 483 | template 484 | struct byte_swapper {}; 485 | 486 | template<> 487 | struct byte_swapper<2> 488 | { 489 | template 490 | T operator()(const T& t) { return MND_BYTE_SWAP_16(t); } 491 | }; 492 | 493 | template<> 494 | struct byte_swapper<4> 495 | { 496 | template 497 | T operator()(const T& t) { return MND_BYTE_SWAP_32(t); } 498 | }; 499 | 500 | template<> 501 | struct byte_swapper<8> 502 | { 503 | template 504 | T operator()(const T& t) { return MND_BYTE_SWAP_64(t); } 505 | }; 506 | 507 | // -- 508 | 509 | #ifndef MND_UNKNOWN_ENDIANNESS 510 | template 511 | struct conditional_reverser 512 | { 513 | template 514 | MND_CONSTEXPR T operator()(const T& t) { return reverse(t); } 515 | }; 516 | 517 | template<> 518 | struct conditional_reverser 519 | { 520 | template 521 | MND_CONSTEXPR T operator()(const T& t) { return t; } 522 | }; 523 | #endif // MND_UNKNOWN_ENDIANNESS 524 | 525 | } // detail 526 | 527 | template 528 | MND_CONSTEXPR T read(InputIt it) noexcept 529 | { 530 | static_assert(detail::is_endian_reversible::value, 531 | "T must be an integral or POD type"); 532 | //static_assert(detail::is_input_iterator::value, 533 | //"Iterator type requirements not met"); 534 | return detail::read(it); 535 | } 536 | 537 | template 538 | MND_CONSTEXPR T read(InputIt it) noexcept 539 | { 540 | static_assert(detail::is_endian_reversible::value, 541 | "T must be an integral or POD type"); 542 | // Read at most `N` bytes from `it`. 543 | return detail::read(it); 544 | } 545 | 546 | template 547 | MND_CONSTEXPR void write(const T& h, OutputIt it) noexcept 548 | { 549 | static_assert(detail::is_endian_reversible::value, 550 | "T must be an integral or POD type"); 551 | //static_assert(detail::is_input_iterator::value, 552 | //"Iterator type requirements not met"); 553 | detail::write(h, it); 554 | } 555 | 556 | template 557 | MND_CONSTEXPR void write(const T& h, OutputIt it) noexcept 558 | { 559 | static_assert(detail::is_endian_reversible::value, 560 | "T must be an integral or POD type"); 561 | detail::write(h, it); 562 | } 563 | 564 | template 565 | MND_CONSTEXPR T reverse(const T& t) 566 | { 567 | return detail::byte_swapper()(t); 568 | } 569 | 570 | #ifndef MND_UNKNOWN_ENDIANNESS 571 | template 572 | MND_CONSTEXPR T conditional_convert(const T& t) noexcept 573 | { 574 | return detail::conditional_reverser()(t); 575 | } 576 | 577 | template 578 | MND_CONSTEXPR T host_to_network(const T& t) 579 | { 580 | return conditional_convert(t); 581 | } 582 | 583 | template 584 | MND_CONSTEXPR T network_to_host(const T& t) 585 | { 586 | // hton and ntoh are essentially the same, as they both do a byte swap if and only 587 | // if the host's and network's byte orders differ. 588 | return host_to_network(t); 589 | } 590 | #endif // MND_UNKNOWN_ENDIANNESS 591 | 592 | } // endian 593 | 594 | #endif // MND_ENDIAN_HEADER 595 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include "endian.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | namespace test { 7 | 8 | template void read1() 9 | { 10 | char buffer[4]; 11 | const int32_t num = 21344; 12 | endian::write(num, buffer); 13 | const int32_t res = endian::read(buffer); 14 | assert(res == num); 15 | } 16 | 17 | template void read2() 18 | { 19 | int32_t buffer; 20 | const int32_t num = 21344; 21 | endian::write(num, reinterpret_cast(&buffer)); 22 | const int32_t res = endian::read( 23 | reinterpret_cast(&buffer)); 24 | assert(res == num); 25 | } 26 | 27 | template void read3() 28 | { 29 | // Buffer size doesn't matter as long as it's at least N large. (N = 3) 30 | char buffer[8]; 31 | const int32_t num = 0x00ffaabb; 32 | endian::write(num, buffer); 33 | // NOTE: don't start reading from the beginning of the buffer as buffer 34 | // contains a 4 byte value and we want 3 bytes. 35 | const int32_t res = endian::read(buffer); 36 | std::printf("expected: 0x%x actual: 0x%x\n", num, res); 37 | assert(res == num); 38 | } 39 | 40 | template void read4() 41 | { 42 | char buffer[4]; 43 | const int32_t num = 0x0000a01f; 44 | endian::write(num, buffer); 45 | const int32_t res = endian::read(buffer); 46 | assert(res == num); 47 | } 48 | 49 | void reverse() 50 | { 51 | const uint32_t orig = 1234; 52 | const uint32_t conv = endian::reverse(endian::reverse(orig)); 53 | assert(conv == orig); 54 | } 55 | 56 | void host_network_conv() 57 | { 58 | const uint32_t orig = 1234; 59 | const uint32_t conv = endian::network_to_host(endian::host_to_network(orig)); 60 | assert(conv == orig); 61 | } 62 | 63 | void typedefs() 64 | { 65 | char buffer[4]; 66 | const int32_t num = 0x0000a01f; 67 | endian::write_le(num, buffer); 68 | auto res1 = endian::read_le<4>(buffer); 69 | assert(res1 == num); 70 | res1 = endian::read_le(buffer); 71 | assert(res1 == num); 72 | 73 | endian::write_be(num, buffer); 74 | auto res2 = endian::read_be<4>(buffer); 75 | assert(res2 == num); 76 | res2 = endian::read_be(buffer); 77 | assert(res2 == num); 78 | } 79 | 80 | } // test 81 | 82 | int main() 83 | { 84 | test::read1(); 85 | test::read1(); 86 | 87 | test::read2(); 88 | test::read2(); 89 | 90 | test::read3(); 91 | test::read3(); 92 | 93 | test::read4(); 94 | test::read4(); 95 | 96 | test::typedefs(); 97 | 98 | test::reverse(); 99 | test::host_network_conv(); 100 | 101 | if(endian::order::host == endian::order::little) 102 | std::printf("host is little endian\n"); 103 | else 104 | std::printf("host is big endian\n"); 105 | } 106 | --------------------------------------------------------------------------------