1//// 2Copyright 2011-2016 Beman Dawes 3 4Distributed under the Boost Software License, Version 1.0. 5(http://www.boost.org/LICENSE_1_0.txt) 6//// 7 8[#arithmetic] 9# Endian Arithmetic Types 10:idprefix: arithmetic_ 11 12## Introduction 13 14Header `boost/endian/arithmetic.hpp` provides integer binary types with 15control over byte order, value type, size, and alignment. Typedefs provide 16easy-to-use names for common configurations. 17 18These types provide portable byte-holders for integer data, independent of 19particular computer architectures. Use cases almost always involve I/O, either 20via files or network connections. Although data portability is the primary 21motivation, these integer byte-holders may also be used to reduce memory use, 22file size, or network activity since they provide binary integer sizes not 23otherwise available. 24 25Such integer byte-holder types are traditionally called *endian* types. See the 26http://en.wikipedia.org/wiki/Endian[Wikipedia] for a full exploration of 27*endianness*, including definitions of *big endian* and *little endian*. 28 29Boost endian integers provide the same full set of {cpp} assignment, arithmetic, 30and relational operators as {cpp} standard integral types, with the standard 31semantics. 32 33Unary arithmetic operators are `+`, `-`, `~`, `!`, plus both prefix and postfix 34`--` and `++`. Binary arithmetic operators are `+`, `+=`, `-`, `-=`, `\*`, 35``*=``, `/`, `/=`, `&`, `&=`, `|`, `|=`, `^`, `^=`, `<<`, `<\<=`, `>>`, and 36`>>=`. Binary relational operators are `==`, `!=`, `<`, `\<=`, `>`, and `>=`. 37 38Implicit conversion to the underlying value type is provided. An implicit 39constructor converting from the underlying value type is provided. 40 41## Example 42 43The `endian_example.cpp` program writes a binary file containing four-byte, 44big-endian and little-endian integers: 45 46``` 47#include <iostream> 48#include <cstdio> 49#include <boost/endian/arithmetic.hpp> 50#include <boost/static_assert.hpp> 51 52using namespace boost::endian; 53 54namespace 55{ 56 // This is an extract from a very widely used GIS file format. 57 // Why the designer decided to mix big and little endians in 58 // the same file is not known. But this is a real-world format 59 // and users wishing to write low level code manipulating these 60 // files have to deal with the mixed endianness. 61 62 struct header 63 { 64 big_int32_t file_code; 65 big_int32_t file_length; 66 little_int32_t version; 67 little_int32_t shape_type; 68 }; 69 70 const char* filename = "test.dat"; 71} 72 73int main(int, char* []) 74{ 75 header h; 76 77 BOOST_STATIC_ASSERT(sizeof(h) == 16U); // reality check 78 79 h.file_code = 0x01020304; 80 h.file_length = sizeof(header); 81 h.version = 1; 82 h.shape_type = 0x01020304; 83 84 // Low-level I/O such as POSIX read/write or <cstdio> 85 // fread/fwrite is sometimes used for binary file operations 86 // when ultimate efficiency is important. Such I/O is often 87 // performed in some C++ wrapper class, but to drive home the 88 // point that endian integers are often used in fairly 89 // low-level code that does bulk I/O operations, <cstdio> 90 // fopen/fwrite is used for I/O in this example. 91 92 std::FILE* fi = std::fopen(filename, "wb"); // MUST BE BINARY 93 94 if (!fi) 95 { 96 std::cout << "could not open " << filename << '\n'; 97 return 1; 98 } 99 100 if (std::fwrite(&h, sizeof(header), 1, fi) != 1) 101 { 102 std::cout << "write failure for " << filename << '\n'; 103 return 1; 104 } 105 106 std::fclose(fi); 107 108 std::cout << "created file " << filename << '\n'; 109 110 return 0; 111} 112``` 113 114After compiling and executing `endian_example.cpp`, a hex dump of `test.dat` 115shows: 116 117``` 11801020304 00000010 01000000 04030201 119``` 120 121Notice that the first two 32-bit integers are big endian while the second two 122are little endian, even though the machine this was compiled and run on was 123little endian. 124 125## Limitations 126 127Requires `<climits>`, `CHAR_BIT == 8`. If `CHAR_BIT` is some other value, 128compilation will result in an `#error`. This restriction is in place because the 129design, implementation, testing, and documentation has only considered issues 130related to 8-bit bytes, and there have been no real-world use cases presented 131for other sizes. 132 133In {cpp}03, `endian_arithmetic` does not meet the requirements for POD types 134because it has constructors, private data members, and a base class. This means 135that common use cases are relying on unspecified behavior in that the {cpp} 136Standard does not guarantee memory layout for non-POD types. This has not been a 137problem in practice since all known {cpp} compilers lay out memory as if 138`endian` were a POD type. In {cpp}11, it is possible to specify the default 139constructor as trivial, and private data members and base classes no longer 140disqualify a type from being a POD type. Thus under {cpp}11, `endian_arithmetic` 141will no longer be relying on unspecified behavior. 142 143## Feature set 144 145* Big endian| little endian | native endian byte ordering. 146* Signed | unsigned 147* Unaligned | aligned 148* 1-8 byte (unaligned) | 1, 2, 4, 8 byte (aligned) 149* Choice of value type 150 151## Enums and typedefs 152 153Two scoped enums are provided: 154 155``` 156enum class order { big, little, native }; 157 158enum class align { no, yes }; 159``` 160 161One class template is provided: 162 163``` 164template <order Order, typename T, std::size_t n_bits, 165 align Align = align::no> 166class endian_arithmetic; 167``` 168 169Typedefs, such as `big_int32_t`, provide convenient naming conventions for 170common use cases: 171 172[%header,cols=5*] 173|=== 174|Name |Alignment |Endianness |Sign |Sizes in bits (n) 175|`big_intN_t` |no |big |signed |8,16,24,32,40,48,56,64 176|`big_uintN_t` |no |big |unsigned |8,16,24,32,40,48,56,64 177|`little_intN_t` |no |little |signed |8,16,24,32,40,48,56,64 178|`little_uintN_t` |no |little |unsigned |8,16,24,32,40,48,56,64 179|`native_intN_t` |no |native |signed |8,16,24,32,40,48,56,64 180|`native_uintN_t` |no |native |unsigned |8,16,24,32,40,48,56,64 181|`big_intN_at` |yes |big |signed |8,16,32,64 182|`big_uintN_at` |yes |big |unsigned |8,16,32,64 183|`little_intN_at` |yes |little |signed |8,16,32,64 184|`little_uintN_at` |yes |little |unsigned |8,16,32,64 185|=== 186 187The unaligned types do not cause compilers to insert padding bytes in classes 188and structs. This is an important characteristic that can be exploited to 189minimize wasted space in memory, files, and network transmissions. 190 191CAUTION: Code that uses aligned types is possibly non-portable because 192alignment requirements vary between hardware architectures and because 193alignment may be affected by compiler switches or pragmas. For example, 194alignment of an 64-bit integer may be to a 32-bit boundary on a 32-bit machine. 195Furthermore, aligned types are only available on architectures with 8, 16, 32, 196and 64-bit integer types. 197 198TIP: Prefer unaligned arithmetic types. 199 200TIP: Protect yourself against alignment ills. For example: 201[none] 202{blank}:: 203+ 204``` 205static_assert(sizeof(containing_struct) == 12, "sizeof(containing_struct) is wrong"); 206``` 207 208NOTE: One-byte arithmetic types have identical layout on all platforms, so they 209never actually reverse endianness. They are provided to enable generic code, 210and to improve code readability and searchability. 211 212## Class template `endian_arithmetic` 213 214`endian_arithmetic` is an integer byte-holder with user-specified endianness, 215value type, size, and alignment. The usual operations on arithmetic types are 216supplied. 217 218### Synopsis 219 220``` 221#include <boost/endian/buffers.hpp> 222 223namespace boost 224{ 225 namespace endian 226 { 227 // C++11 features emulated if not available 228 229 enum class align { no, yes }; 230 231 template <order Order, class T, std::size_t n_bits, 232 align Align = align::no> 233 class endian_arithmetic 234 { 235 public: 236 237 typedef T value_type; 238 239 // if BOOST_ENDIAN_FORCE_PODNESS is defined && C++11 PODs are not 240 // available then these two constructors will not be present 241 endian_arithmetic() noexcept = default; 242 endian_arithmetic(T v) noexcept; 243 244 endian_arithmetic& operator=(T v) noexcept; 245 operator value_type() const noexcept; 246 value_type value() const noexcept; 247 unsigned char* data() noexcept; 248 unsigned char const* data() const noexcept; 249 250 // arithmetic operations 251 // note that additional operations are provided by the value_type 252 value_type operator+() const noexcept; 253 endian_arithmetic& operator+=(value_type y) noexcept; 254 endian_arithmetic& operator-=(value_type y) noexcept; 255 endian_arithmetic& operator*=(value_type y) noexcept; 256 endian_arithmetic& operator/=(value_type y) noexcept; 257 endian_arithmetic& operator%=(value_type y) noexcept; 258 endian_arithmetic& operator&=(value_type y) noexcept; 259 endian_arithmetic& operator|=(value_type y) noexcept; 260 endian_arithmetic& operator^=(value_type y) noexcept; 261 endian_arithmetic& operator<<=(value_type y) noexcept; 262 endian_arithmetic& operator>>=(value_type y) noexcept; 263 endian_arithmetic& operator++() noexcept; 264 endian_arithmetic& operator--() noexcept; 265 endian_arithmetic operator++(int) noexcept; 266 endian_arithmetic operator--(int) noexcept; 267 268 // Stream inserter 269 template <class charT, class traits> 270 friend std::basic_ostream<charT, traits>& 271 operator<<(std::basic_ostream<charT, traits>& os, const endian_arithmetic& x); 272 273 // Stream extractor 274 template <class charT, class traits> 275 friend std::basic_istream<charT, traits>& 276 operator>>(std::basic_istream<charT, traits>& is, endian_arithmetic& x); 277 }; 278 279 // typedefs 280 281 // unaligned big endian signed integer types 282 typedef endian_arithmetic<order::big, int_least8_t, 8> big_int8_t; 283 typedef endian_arithmetic<order::big, int_least16_t, 16> big_int16_t; 284 typedef endian_arithmetic<order::big, int_least32_t, 24> big_int24_t; 285 typedef endian_arithmetic<order::big, int_least32_t, 32> big_int32_t; 286 typedef endian_arithmetic<order::big, int_least64_t, 40> big_int40_t; 287 typedef endian_arithmetic<order::big, int_least64_t, 48> big_int48_t; 288 typedef endian_arithmetic<order::big, int_least64_t, 56> big_int56_t; 289 typedef endian_arithmetic<order::big, int_least64_t, 64> big_int64_t; 290 291 // unaligned big endian unsigned integer types 292 typedef endian_arithmetic<order::big, uint_least8_t, 8> big_uint8_t; 293 typedef endian_arithmetic<order::big, uint_least16_t, 16> big_uint16_t; 294 typedef endian_arithmetic<order::big, uint_least32_t, 24> big_uint24_t; 295 typedef endian_arithmetic<order::big, uint_least32_t, 32> big_uint32_t; 296 typedef endian_arithmetic<order::big, uint_least64_t, 40> big_uint40_t; 297 typedef endian_arithmetic<order::big, uint_least64_t, 48> big_uint48_t; 298 typedef endian_arithmetic<order::big, uint_least64_t, 56> big_uint56_t; 299 typedef endian_arithmetic<order::big, uint_least64_t, 64> big_uint64_t; 300 301 // unaligned big endian floating point types 302 typedef endian_arithmetic<order::big, float, 32> big_float32_t; 303 typedef endian_arithmetic<order::big, double, 64> big_float64_t; 304 305 // unaligned little endian signed integer types 306 typedef endian_arithmetic<order::little, int_least8_t, 8> little_int8_t; 307 typedef endian_arithmetic<order::little, int_least16_t, 16> little_int16_t; 308 typedef endian_arithmetic<order::little, int_least32_t, 24> little_int24_t; 309 typedef endian_arithmetic<order::little, int_least32_t, 32> little_int32_t; 310 typedef endian_arithmetic<order::little, int_least64_t, 40> little_int40_t; 311 typedef endian_arithmetic<order::little, int_least64_t, 48> little_int48_t; 312 typedef endian_arithmetic<order::little, int_least64_t, 56> little_int56_t; 313 typedef endian_arithmetic<order::little, int_least64_t, 64> little_int64_t; 314 315 // unaligned little endian unsigned integer types 316 typedef endian_arithmetic<order::little, uint_least8_t, 8> little_uint8_t; 317 typedef endian_arithmetic<order::little, uint_least16_t, 16> little_uint16_t; 318 typedef endian_arithmetic<order::little, uint_least32_t, 24> little_uint24_t; 319 typedef endian_arithmetic<order::little, uint_least32_t, 32> little_uint32_t; 320 typedef endian_arithmetic<order::little, uint_least64_t, 40> little_uint40_t; 321 typedef endian_arithmetic<order::little, uint_least64_t, 48> little_uint48_t; 322 typedef endian_arithmetic<order::little, uint_least64_t, 56> little_uint56_t; 323 typedef endian_arithmetic<order::little, uint_least64_t, 64> little_uint64_t; 324 325 // unaligned little endian floating point types 326 typedef endian_arithmetic<order::little, float, 32> little_float32_t; 327 typedef endian_arithmetic<order::little, double, 64> little_float64_t; 328 329 // unaligned native endian signed integer types 330 typedef endian_arithmetic<order::native, int_least8_t, 8> native_int8_t; 331 typedef endian_arithmetic<order::native, int_least16_t, 16> native_int16_t; 332 typedef endian_arithmetic<order::native, int_least32_t, 24> native_int24_t; 333 typedef endian_arithmetic<order::native, int_least32_t, 32> native_int32_t; 334 typedef endian_arithmetic<order::native, int_least64_t, 40> native_int40_t; 335 typedef endian_arithmetic<order::native, int_least64_t, 48> native_int48_t; 336 typedef endian_arithmetic<order::native, int_least64_t, 56> native_int56_t; 337 typedef endian_arithmetic<order::native, int_least64_t, 64> native_int64_t; 338 339 // unaligned native endian unsigned integer types 340 typedef endian_arithmetic<order::native, uint_least8_t, 8> native_uint8_t; 341 typedef endian_arithmetic<order::native, uint_least16_t, 16> native_uint16_t; 342 typedef endian_arithmetic<order::native, uint_least32_t, 24> native_uint24_t; 343 typedef endian_arithmetic<order::native, uint_least32_t, 32> native_uint32_t; 344 typedef endian_arithmetic<order::native, uint_least64_t, 40> native_uint40_t; 345 typedef endian_arithmetic<order::native, uint_least64_t, 48> native_uint48_t; 346 typedef endian_arithmetic<order::native, uint_least64_t, 56> native_uint56_t; 347 typedef endian_arithmetic<order::native, uint_least64_t, 64> native_uint64_t; 348 349 // unaligned native endian floating point types 350 typedef endian_arithmetic<order::native, float, 32> native_float32_t; 351 typedef endian_arithmetic<order::native, double, 64> native_float64_t; 352 353 // aligned big endian signed integer types 354 typedef endian_arithmetic<order::big, int8_t, 8, align::yes> big_int8_at; 355 typedef endian_arithmetic<order::big, int16_t, 16, align::yes> big_int16_at; 356 typedef endian_arithmetic<order::big, int32_t, 32, align::yes> big_int32_at; 357 typedef endian_arithmetic<order::big, int64_t, 64, align::yes> big_int64_at; 358 359 // aligned big endian unsigned integer types 360 typedef endian_arithmetic<order::big, uint8_t, 8, align::yes> big_uint8_at; 361 typedef endian_arithmetic<order::big, uint16_t, 16, align::yes> big_uint16_at; 362 typedef endian_arithmetic<order::big, uint32_t, 32, align::yes> big_uint32_at; 363 typedef endian_arithmetic<order::big, uint64_t, 64, align::yes> big_uint64_at; 364 365 // aligned big endian floating point types 366 typedef endian_arithmetic<order::big, float, 32, align::yes> big_float32_at; 367 typedef endian_arithmetic<order::big, double, 64, align::yes> big_float64_at; 368 369 // aligned little endian signed integer types 370 typedef endian_arithmetic<order::little, int8_t, 8, align::yes> little_int8_at; 371 typedef endian_arithmetic<order::little, int16_t, 16, align::yes> little_int16_at; 372 typedef endian_arithmetic<order::little, int32_t, 32, align::yes> little_int32_at; 373 typedef endian_arithmetic<order::little, int64_t, 64, align::yes> little_int64_at; 374 375 // aligned little endian unsigned integer types 376 typedef endian_arithmetic<order::little, uint8_t, 8, align::yes> little_uint8_at; 377 typedef endian_arithmetic<order::little, uint16_t, 16, align::yes> little_uint16_at; 378 typedef endian_arithmetic<order::little, uint32_t, 32, align::yes> little_uint32_at; 379 typedef endian_arithmetic<order::little, uint64_t, 64, align::yes> little_uint64_at; 380 381 // aligned little endian floating point types 382 typedef endian_arithmetic<order::little, float, 32, align::yes> little_float32_at; 383 typedef endian_arithmetic<order::little, double, 64, align::yes> little_float64_at; 384 385 // aligned native endian typedefs are not provided because 386 // <cstdint> types are superior for that use case 387 388 } // namespace endian 389} // namespace boost 390``` 391 392The only supported value of `CHAR_BIT` is 8. 393 394The valid values of `Nbits` are as follows: 395 396* When `sizeof(T)` is 1, `Nbits` shall be 8; 397* When `sizeof(T)` is 2, `Nbits` shall be 16; 398* When `sizeof(T)` is 4, `Nbits` shall be 24 or 32; 399* When `sizeof(T)` is 8, `Nbits` shall be 40, 48, 56, or 64. 400 401Other values of `sizeof(T)` are not supported. 402 403When `Nbits` is equal to `sizeof(T)*8`, `T` must be a standard arithmetic type. 404 405When `Nbits` is less than `sizeof(T)*8`, `T` must be a standard integral type 406({cpp}std, [basic.fundamental]) that is not `bool`. 407 408### Members 409 410``` 411endian_arithmetic() noexcept = default; // C++03: endian(){} 412``` 413[none] 414* {blank} 415+ 416Effects:: Constructs an uninitialized object. 417 418``` 419endian_arithmetic(T v) noexcept; 420``` 421[none] 422* {blank} 423+ 424Effects:: See `endian_buffer::endian_buffer(T)`. 425 426``` 427endian_arithmetic& operator=(T v) noexcept; 428``` 429[none] 430* {blank} 431+ 432Effects:: See `endian_buffer::operator=(T)`. 433Returns:: `*this`. 434 435``` 436value_type value() const noexcept; 437``` 438[none] 439* {blank} 440+ 441Returns:: See `endian_buffer::value()`. 442 443``` 444unsigned char* data() noexcept; 445``` 446``` 447unsigned char const* data() const noexcept; 448``` 449[none] 450* {blank} 451+ 452Returns:: See `endian_buffer::data()`. 453 454``` 455operator T() const noexcept; 456``` 457[none] 458* {blank} 459+ 460Returns:: 461 `value()`. 462 463### Other operators 464 465Other operators on endian objects are forwarded to the equivalent operator on 466`value_type`. 467 468### Stream inserter 469 470``` 471template <class charT, class traits> 472friend std::basic_ostream<charT, traits>& 473 operator<<(std::basic_ostream<charT, traits>& os, const endian_arithmetic& x); 474 475``` 476[none] 477* {blank} 478+ 479Returns:: `os << +x`. 480[none] 481 482### Stream extractor 483 484``` 485template <class charT, class traits> 486friend std::basic_istream<charT, traits>& 487 operator>>(std::basic_istream<charT, traits>& is, endian_arithmetic& x); 488``` 489[none] 490* {blank} 491+ 492Effects:: As if: 493+ 494``` 495T i; 496if (is >> i) 497 x = i; 498``` 499Returns:: `is`. 500 501## FAQ 502 503See the <<overview_faq,Overview FAQ>> for a library-wide FAQ. 504 505Why not just use Boost.Serialization?:: 506Serialization involves a conversion for every object involved in I/O. Endian 507integers require no conversion or copying. They are already in the desired 508format for binary I/O. Thus they can be read or written in bulk. 509 510Are endian types PODs?:: 511Yes for {cpp}11. No for {cpp}03, although several 512<<arithmetic_compilation,macros>> are available to force PODness in all cases. 513 514What are the implications of endian integer types not being PODs with {cpp}03 compilers?:: 515They can't be used in unions. Also, compilers aren't required to align or lay 516out storage in portable ways, although this potential problem hasn't prevented 517use of Boost.Endian with real compilers. 518 519What good is native endianness?:: 520It provides alignment and size guarantees not available from the built-in 521types. It eases generic programming. 522 523Why bother with the aligned endian types?:: 524Aligned integer operations may be faster (as much as 10 to 20 times faster) 525if the endianness and alignment of the type matches the endianness and 526alignment requirements of the machine. The code, however, will be somewhat less 527portable than with the unaligned types. 528 529Why provide the arithmetic operations?:: 530Providing a full set of operations reduces program clutter and makes code 531both easier to write and to read. Consider incrementing a variable in a record. 532It is very convenient to write: 533+ 534``` 535++record.foo; 536``` 537+ 538Rather than: 539+ 540``` 541int temp(record.foo); 542++temp; 543record.foo = temp; 544``` 545 546## Design considerations for Boost.Endian types 547 548* Must be suitable for I/O - in other words, must be memcpyable. 549* Must provide exactly the size and internal byte ordering specified. 550* Must work correctly when the internal integer representation has more bits 551that the sum of the bits in the external byte representation. Sign extension 552must work correctly when the internal integer representation type has more 553bits than the sum of the bits in the external bytes. For example, using 554a 64-bit integer internally to represent 40-bit (5 byte) numbers must work for 555both positive and negative values. 556* Must work correctly (including using the same defined external 557representation) regardless of whether a compiler treats char as signed or 558unsigned. 559* Unaligned types must not cause compilers to insert padding bytes. 560* The implementation should supply optimizations with great care. Experience 561has shown that optimizations of endian integers often become pessimizations 562when changing machines or compilers. Pessimizations can also happen when 563changing compiler switches, compiler versions, or CPU models of the same 564architecture. 565 566## Experience 567 568Classes with similar functionality have been independently developed by 569several Boost programmers and used very successful in high-value, high-use 570applications for many years. These independently developed endian libraries 571often evolved from C libraries that were also widely used. Endian types have 572proven widely useful across a wide range of computer architectures and 573applications. 574 575## Motivating use cases 576 577Neil Mayhew writes: "I can also provide a meaningful use-case for this 578library: reading TrueType font files from disk and processing the contents. The 579data format has fixed endianness (big) and has unaligned values in various 580places. Using Boost.Endian simplifies and cleans the code wonderfully." 581 582## {cpp}11 583 584The availability of the {cpp}11 585http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm[Defaulted 586Functions] feature is detected automatically, and will be used if present to 587ensure that objects of `class endian_arithmetic` are trivial, and thus PODs. 588 589## Compilation 590 591Boost.Endian is implemented entirely within headers, with no need to link to any 592Boost object libraries. 593 594Several macros allow user control over features: 595 596* BOOST_ENDIAN_NO_CTORS causes `class endian_arithmetic` to have no 597constructors. The intended use is for compiling user code that must be portable 598between compilers regardless of {cpp}11 599http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm[Defaulted 600Functions] support. Use of constructors will always fail, 601* BOOST_ENDIAN_FORCE_PODNESS causes BOOST_ENDIAN_NO_CTORS to be defined if 602the compiler does not support {cpp}11 603http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm[Defaulted 604Functions]. This is ensures that objects of `class endian_arithmetic` are PODs, 605and so can be used in {cpp}03 unions. In {cpp}11, `class endian_arithmetic` 606objects are PODs, even though they have constructors, so can always be used in 607unions. 608 609## Acknowledgements 610 611Original design developed by Darin Adler based on classes developed by Mark 612Borgerding. Four original class templates combined into a single 613`endian_arithmetic` class template by Beman Dawes, who put the library together, 614provided documentation, added the typedefs, and also added the 615`unrolled_byte_loops` sign partial specialization to correctly extend the sign 616when cover integer size differs from endian representation size. 617