1 // Boost endian.hpp header file -------------------------------------------------------// 2 3 // (C) Copyright Darin Adler 2000 4 // (C) Copyright Beman Dawes 2006, 2009 5 6 // Distributed under the Boost Software License, Version 1.0. 7 // See http://www.boost.org/LICENSE_1_0.txt 8 9 // See library home page at http://www.boost.org/libs/endian 10 11 //--------------------------------------------------------------------------------------// 12 13 // Original design developed by Darin Adler based on classes developed by Mark 14 // Borgerding. Four original class templates were combined into a single endian 15 // class template by Beman Dawes, who also added the unrolled_byte_loops sign 16 // partial specialization to correctly extend the sign when cover integer size 17 // differs from endian representation size. 18 19 // TODO: When a compiler supporting constexpr becomes available, try possible uses. 20 21 #ifndef BOOST_SPIRIT_ENDIAN_HPP 22 #define BOOST_SPIRIT_ENDIAN_HPP 23 24 #if defined(_MSC_VER) 25 #pragma once 26 #endif 27 28 #ifdef BOOST_ENDIAN_LOG 29 # include <iostream> 30 #endif 31 32 #if defined(__BORLANDC__) || defined( __CODEGEARC__) 33 # pragma pack(push, 1) 34 #endif 35 36 #include <boost/config.hpp> 37 #include <boost/predef/other/endian.h> 38 #ifndef BOOST_MINIMAL_INTEGER_COVER_OPERATORS 39 #define BOOST_MINIMAL_INTEGER_COVER_OPERATORS 40 #endif 41 #ifndef BOOST_NO_IO_COVER_OPERATORS 42 #define BOOST_NO_IO_COVER_OPERATORS 43 #endif 44 #include <boost/spirit/home/support/detail/endian/cover_operators.hpp> 45 #undef BOOST_NO_IO_COVER_OPERATORS 46 #undef BOOST_MINIMAL_INTEGER_COVER_OPERATORS 47 #include <boost/type_traits/is_signed.hpp> 48 #include <boost/type_traits/make_unsigned.hpp> 49 #include <boost/cstdint.hpp> 50 #include <boost/static_assert.hpp> 51 #include <boost/spirit/home/support/detail/scoped_enum_emulation.hpp> 52 #include <iosfwd> 53 #include <climits> 54 55 # if CHAR_BIT != 8 56 # error Platforms with CHAR_BIT != 8 are not supported 57 # endif 58 59 # define BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT {} // C++03 60 61 # if defined(BOOST_ENDIAN_NO_CTORS) || defined(BOOST_ENDIAN_FORCE_PODNESS) 62 # define BOOST_SPIRIT_ENDIAN_NO_CTORS 63 # endif 64 65 66 namespace boost { namespace spirit 67 { 68 namespace detail 69 { 70 // Unrolled loops for loading and storing streams of bytes. 71 72 template <typename T, std::size_t n_bytes, 73 bool sign=boost::is_signed<T>::value > 74 struct unrolled_byte_loops 75 { 76 typedef unrolled_byte_loops<T, n_bytes - 1, sign> next; 77 load_bigboost::spirit::detail::unrolled_byte_loops78 static typename boost::make_unsigned<T>::type load_big(const unsigned char* bytes) 79 { return *(bytes - 1) | (next::load_big(bytes - 1) << 8); } load_littleboost::spirit::detail::unrolled_byte_loops80 static typename boost::make_unsigned<T>::type load_little(const unsigned char* bytes) 81 { return *bytes | (next::load_little(bytes + 1) << 8); } 82 store_bigboost::spirit::detail::unrolled_byte_loops83 static void store_big(char* bytes, T value) 84 { 85 *(bytes - 1) = static_cast<char>(value); 86 next::store_big(bytes - 1, value >> 8); 87 } store_littleboost::spirit::detail::unrolled_byte_loops88 static void store_little(char* bytes, T value) 89 { 90 *bytes = static_cast<char>(value); 91 next::store_little(bytes + 1, value >> 8); 92 } 93 }; 94 95 template <typename T> 96 struct unrolled_byte_loops<T, 1, false> 97 { load_bigboost::spirit::detail::unrolled_byte_loops98 static T load_big(const unsigned char* bytes) 99 { return *(bytes - 1); } load_littleboost::spirit::detail::unrolled_byte_loops100 static T load_little(const unsigned char* bytes) 101 { return *bytes; } store_bigboost::spirit::detail::unrolled_byte_loops102 static void store_big(char* bytes, T value) 103 { *(bytes - 1) = static_cast<char>(value); } store_littleboost::spirit::detail::unrolled_byte_loops104 static void store_little(char* bytes, T value) 105 { *bytes = static_cast<char>(value); } 106 107 }; 108 109 template <typename T> 110 struct unrolled_byte_loops<T, 1, true> 111 { load_bigboost::spirit::detail::unrolled_byte_loops112 static typename boost::make_unsigned<T>::type load_big(const unsigned char* bytes) 113 { return *(bytes - 1); } load_littleboost::spirit::detail::unrolled_byte_loops114 static typename boost::make_unsigned<T>::type load_little(const unsigned char* bytes) 115 { return *bytes; } store_bigboost::spirit::detail::unrolled_byte_loops116 static void store_big(char* bytes, T value) 117 { *(bytes - 1) = static_cast<char>(value); } store_littleboost::spirit::detail::unrolled_byte_loops118 static void store_little(char* bytes, T value) 119 { *bytes = static_cast<char>(value); } 120 }; 121 122 template <typename T, std::size_t n_bytes> 123 inline load_big_endian(const void * bytes)124 T load_big_endian(const void* bytes) 125 { 126 return static_cast<T>(unrolled_byte_loops<T, n_bytes>::load_big 127 (static_cast<const unsigned char*>(bytes) + n_bytes)); 128 } 129 130 template <> 131 inline load_big_endian(const void * bytes)132 float load_big_endian<float, 4>(const void* bytes) 133 { 134 const unsigned char *b = reinterpret_cast<const unsigned char *>( 135 bytes); 136 b += 3; 137 138 float value; 139 unsigned char *v = reinterpret_cast<unsigned char *>(&value); 140 141 for(std::size_t i = 0; i < 4; ++i) 142 { 143 *v++ = *b--; 144 } 145 146 return value; 147 } 148 149 template <> 150 inline load_big_endian(const void * bytes)151 double load_big_endian<double, 8>(const void* bytes) 152 { 153 const unsigned char *b = reinterpret_cast<const unsigned char *>( 154 bytes); 155 b += 7; 156 157 double value; 158 unsigned char *v = reinterpret_cast<unsigned char *>(&value); 159 160 for(std::size_t i = 0; i < 8; ++i) 161 { 162 *v++ = *b--; 163 } 164 165 return value; 166 } 167 168 template <typename T, std::size_t n_bytes> 169 inline load_little_endian(const void * bytes)170 T load_little_endian(const void* bytes) 171 { 172 return static_cast<T>(unrolled_byte_loops<T, n_bytes>::load_little 173 (static_cast<const unsigned char*>(bytes))); 174 } 175 176 template <> 177 inline load_little_endian(const void * bytes)178 float load_little_endian<float, 4>(const void* bytes) 179 { 180 const unsigned char *b = reinterpret_cast<const unsigned char *>( 181 bytes); 182 183 float value; 184 unsigned char *v = reinterpret_cast<unsigned char *>(&value); 185 186 for(std::size_t i = 0; i < 4; ++i) 187 { 188 *v++ = *b++; 189 } 190 191 return value; 192 } 193 194 template <> 195 inline load_little_endian(const void * bytes)196 double load_little_endian<double, 8>(const void* bytes) 197 { 198 const unsigned char *b = reinterpret_cast<const unsigned char *>( 199 bytes); 200 201 double value; 202 unsigned char *v = reinterpret_cast<unsigned char *>(&value); 203 204 for(std::size_t i = 0; i < 8; ++i) 205 { 206 *v++ = *b++; 207 } 208 209 return value; 210 } 211 212 template <typename T, std::size_t n_bytes> 213 inline store_big_endian(void * bytes,T value)214 void store_big_endian(void* bytes, T value) 215 { 216 unrolled_byte_loops<T, n_bytes>::store_big 217 (static_cast<char*>(bytes) + n_bytes, value); 218 } 219 220 template <> 221 inline store_big_endian(void * bytes,float value)222 void store_big_endian<float, 4>(void* bytes, float value) 223 { 224 unsigned char *b = reinterpret_cast<unsigned char *>(bytes); 225 b += 3; 226 227 const unsigned char *v = reinterpret_cast<const unsigned char *>( 228 &value); 229 230 for(std::size_t i = 0; i < 4; ++i) 231 { 232 *b-- = *v++; 233 } 234 } 235 236 template <> 237 inline store_big_endian(void * bytes,double value)238 void store_big_endian<double, 8>(void* bytes, double value) 239 { 240 unsigned char *b = reinterpret_cast<unsigned char *>(bytes); 241 b += 7; 242 243 const unsigned char *v = reinterpret_cast<const unsigned char *>( 244 &value); 245 246 for(std::size_t i = 0; i < 8; ++i) 247 { 248 *b-- = *v++; 249 } 250 } 251 252 template <typename T, std::size_t n_bytes> 253 inline store_little_endian(void * bytes,T value)254 void store_little_endian(void* bytes, T value) 255 { 256 unrolled_byte_loops<T, n_bytes>::store_little 257 (static_cast<char*>(bytes), value); 258 } 259 260 template <> 261 inline store_little_endian(void * bytes,float value)262 void store_little_endian<float, 4>(void* bytes, float value) 263 { 264 unsigned char *b = reinterpret_cast<unsigned char *>(bytes); 265 266 const unsigned char *v = reinterpret_cast<const unsigned char *>( 267 &value); 268 269 for(std::size_t i = 0; i < 4; ++i) 270 { 271 *b++ = *v++; 272 } 273 } 274 275 template <> 276 inline store_little_endian(void * bytes,double value)277 void store_little_endian<double, 8>(void* bytes, double value) 278 { 279 unsigned char *b = reinterpret_cast<unsigned char *>(bytes); 280 281 const unsigned char *v = reinterpret_cast<const unsigned char *>( 282 &value); 283 284 for(std::size_t i = 0; i < 8; ++i) 285 { 286 *b++ = *v++; 287 } 288 } 289 290 } // namespace detail 291 292 namespace endian 293 { 294 295 # ifdef BOOST_ENDIAN_LOG 296 bool endian_log(true); 297 # endif 298 299 300 // endian class template and specializations ---------------------------------------// 301 BOOST_SCOPED_ENUM_START(endianness)302 BOOST_SCOPED_ENUM_START(endianness) { big, little, native }; BOOST_SCOPED_ENUM_END BOOST_SCOPED_ENUM_START(alignment)303 BOOST_SCOPED_ENUM_START(alignment) { unaligned, aligned }; BOOST_SCOPED_ENUM_END 304 305 template <BOOST_SCOPED_ENUM(endianness) E, typename T, std::size_t n_bits, 306 BOOST_SCOPED_ENUM(alignment) A = alignment::unaligned> 307 class endian; 308 309 // Specializations that represent unaligned bytes. 310 // Taking an integer type as a parameter provides a nice way to pass both 311 // the size and signedness of the desired integer and get the appropriate 312 // corresponding integer type for the interface. 313 314 // unaligned big endian specialization 315 template <typename T, std::size_t n_bits> 316 class endian< endianness::big, T, n_bits, alignment::unaligned > 317 : cover_operators< endian< endianness::big, T, n_bits >, T > 318 { 319 BOOST_STATIC_ASSERT( (n_bits/8)*8 == n_bits ); 320 public: 321 typedef T value_type; 322 # ifndef BOOST_SPIRIT_ENDIAN_NO_CTORS endian()323 endian() BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 324 explicit endian(T val) 325 { 326 # ifdef BOOST_ENDIAN_LOG 327 if ( endian_log ) 328 std::clog << "big, unaligned, " << n_bits << "-bits, construct(" << val << ")\n"; 329 # endif 330 detail::store_big_endian<T, n_bits/8>(m_value, val); 331 } 332 # endif operator =(T val)333 endian & operator=(T val) { detail::store_big_endian<T, n_bits/8>(m_value, val); return *this; } operator T() const334 operator T() const 335 { 336 # ifdef BOOST_ENDIAN_LOG 337 if ( endian_log ) 338 std::clog << "big, unaligned, " << n_bits << "-bits, convert(" << detail::load_big_endian<T, n_bits/8>(m_value) << ")\n"; 339 # endif 340 return detail::load_big_endian<T, n_bits/8>(m_value); 341 } 342 private: 343 char m_value[n_bits/8]; 344 }; 345 346 // unaligned little endian specialization 347 template <typename T, std::size_t n_bits> 348 class endian< endianness::little, T, n_bits, alignment::unaligned > 349 : cover_operators< endian< endianness::little, T, n_bits >, T > 350 { 351 BOOST_STATIC_ASSERT( (n_bits/8)*8 == n_bits ); 352 public: 353 typedef T value_type; 354 # ifndef BOOST_SPIRIT_ENDIAN_NO_CTORS endian()355 endian() BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 356 explicit endian(T val) 357 { 358 # ifdef BOOST_ENDIAN_LOG 359 if ( endian_log ) 360 std::clog << "little, unaligned, " << n_bits << "-bits, construct(" << val << ")\n"; 361 # endif 362 detail::store_little_endian<T, n_bits/8>(m_value, val); 363 } 364 # endif operator =(T val)365 endian & operator=(T val) { detail::store_little_endian<T, n_bits/8>(m_value, val); return *this; } operator T() const366 operator T() const 367 { 368 # ifdef BOOST_ENDIAN_LOG 369 if ( endian_log ) 370 std::clog << "little, unaligned, " << n_bits << "-bits, convert(" << detail::load_little_endian<T, n_bits/8>(m_value) << ")\n"; 371 # endif 372 return detail::load_little_endian<T, n_bits/8>(m_value); 373 } 374 private: 375 char m_value[n_bits/8]; 376 }; 377 378 // unaligned native endian specialization 379 template <typename T, std::size_t n_bits> 380 class endian< endianness::native, T, n_bits, alignment::unaligned > 381 : cover_operators< endian< endianness::native, T, n_bits >, T > 382 { 383 BOOST_STATIC_ASSERT( (n_bits/8)*8 == n_bits ); 384 public: 385 typedef T value_type; 386 # ifndef BOOST_SPIRIT_ENDIAN_NO_CTORS endian()387 endian() BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 388 # if BOOST_ENDIAN_BIG_BYTE 389 explicit endian(T val) { detail::store_big_endian<T, n_bits/8>(m_value, val); } 390 # else 391 explicit endian(T val) { detail::store_little_endian<T, n_bits/8>(m_value, val); } 392 # endif 393 # endif 394 # if BOOST_ENDIAN_BIG_BYTE operator =(T val)395 endian & operator=(T val) { detail::store_big_endian<T, n_bits/8>(m_value, val); return *this; } operator T() const396 operator T() const { return detail::load_big_endian<T, n_bits/8>(m_value); } 397 # else operator =(T val)398 endian & operator=(T val) { detail::store_little_endian<T, n_bits/8>(m_value, val); return *this; } operator T() const399 operator T() const { return detail::load_little_endian<T, n_bits/8>(m_value); } 400 # endif 401 private: 402 char m_value[n_bits/8]; 403 }; 404 405 // Specializations that mimic built-in integer types. 406 // These typically have the same alignment as the underlying types. 407 408 // aligned big endian specialization 409 template <typename T, std::size_t n_bits> 410 class endian< endianness::big, T, n_bits, alignment::aligned > 411 : cover_operators< endian< endianness::big, T, n_bits, alignment::aligned >, T > 412 { 413 BOOST_STATIC_ASSERT( (n_bits/8)*8 == n_bits ); 414 BOOST_STATIC_ASSERT( sizeof(T) == n_bits/8 ); 415 public: 416 typedef T value_type; 417 # ifndef BOOST_SPIRIT_ENDIAN_NO_CTORS endian()418 endian() BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 419 # if BOOST_ENDIAN_BIG_BYTE 420 endian(T val) : m_value(val) { } 421 # else 422 explicit endian(T val) { detail::store_big_endian<T, sizeof(T)>(&m_value, val); } 423 # endif 424 # endif 425 # if BOOST_ENDIAN_BIG_BYTE operator =(T val)426 endian & operator=(T val) { m_value = val; return *this; } operator T() const427 operator T() const { return m_value; } 428 # else operator =(T val)429 endian & operator=(T val) { detail::store_big_endian<T, sizeof(T)>(&m_value, val); return *this; } operator T() const430 operator T() const { return detail::load_big_endian<T, sizeof(T)>(&m_value); } 431 # endif 432 private: 433 T m_value; 434 }; 435 436 // aligned little endian specialization 437 template <typename T, std::size_t n_bits> 438 class endian< endianness::little, T, n_bits, alignment::aligned > 439 : cover_operators< endian< endianness::little, T, n_bits, alignment::aligned >, T > 440 { 441 BOOST_STATIC_ASSERT( (n_bits/8)*8 == n_bits ); 442 BOOST_STATIC_ASSERT( sizeof(T) == n_bits/8 ); 443 public: 444 typedef T value_type; 445 # ifndef BOOST_SPIRIT_ENDIAN_NO_CTORS endian()446 endian() BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 447 # if BOOST_ENDIAN_LITTLE_BYTE 448 endian(T val) : m_value(val) { } 449 # else 450 explicit endian(T val) { detail::store_little_endian<T, sizeof(T)>(&m_value, val); } 451 # endif 452 # endif 453 # if BOOST_ENDIAN_LITTLE_BYTE operator =(T val)454 endian & operator=(T val) { m_value = val; return *this; } operator T() const455 operator T() const { return m_value; } 456 #else operator =(T val)457 endian & operator=(T val) { detail::store_little_endian<T, sizeof(T)>(&m_value, val); return *this; } operator T() const458 operator T() const { return detail::load_little_endian<T, sizeof(T)>(&m_value); } 459 #endif 460 private: 461 T m_value; 462 }; 463 464 // naming convention typedefs ------------------------------------------------------// 465 466 // unaligned big endian signed integer types 467 typedef endian< endianness::big, int_least8_t, 8 > big8_t; 468 typedef endian< endianness::big, int_least16_t, 16 > big16_t; 469 typedef endian< endianness::big, int_least32_t, 24 > big24_t; 470 typedef endian< endianness::big, int_least32_t, 32 > big32_t; 471 typedef endian< endianness::big, int_least64_t, 40 > big40_t; 472 typedef endian< endianness::big, int_least64_t, 48 > big48_t; 473 typedef endian< endianness::big, int_least64_t, 56 > big56_t; 474 typedef endian< endianness::big, int_least64_t, 64 > big64_t; 475 476 // unaligned big endian unsigned integer types 477 typedef endian< endianness::big, uint_least8_t, 8 > ubig8_t; 478 typedef endian< endianness::big, uint_least16_t, 16 > ubig16_t; 479 typedef endian< endianness::big, uint_least32_t, 24 > ubig24_t; 480 typedef endian< endianness::big, uint_least32_t, 32 > ubig32_t; 481 typedef endian< endianness::big, uint_least64_t, 40 > ubig40_t; 482 typedef endian< endianness::big, uint_least64_t, 48 > ubig48_t; 483 typedef endian< endianness::big, uint_least64_t, 56 > ubig56_t; 484 typedef endian< endianness::big, uint_least64_t, 64 > ubig64_t; 485 486 // unaligned little endian signed integer types 487 typedef endian< endianness::little, int_least8_t, 8 > little8_t; 488 typedef endian< endianness::little, int_least16_t, 16 > little16_t; 489 typedef endian< endianness::little, int_least32_t, 24 > little24_t; 490 typedef endian< endianness::little, int_least32_t, 32 > little32_t; 491 typedef endian< endianness::little, int_least64_t, 40 > little40_t; 492 typedef endian< endianness::little, int_least64_t, 48 > little48_t; 493 typedef endian< endianness::little, int_least64_t, 56 > little56_t; 494 typedef endian< endianness::little, int_least64_t, 64 > little64_t; 495 496 // unaligned little endian unsigned integer types 497 typedef endian< endianness::little, uint_least8_t, 8 > ulittle8_t; 498 typedef endian< endianness::little, uint_least16_t, 16 > ulittle16_t; 499 typedef endian< endianness::little, uint_least32_t, 24 > ulittle24_t; 500 typedef endian< endianness::little, uint_least32_t, 32 > ulittle32_t; 501 typedef endian< endianness::little, uint_least64_t, 40 > ulittle40_t; 502 typedef endian< endianness::little, uint_least64_t, 48 > ulittle48_t; 503 typedef endian< endianness::little, uint_least64_t, 56 > ulittle56_t; 504 typedef endian< endianness::little, uint_least64_t, 64 > ulittle64_t; 505 506 // unaligned native endian signed integer types 507 typedef endian< endianness::native, int_least8_t, 8 > native8_t; 508 typedef endian< endianness::native, int_least16_t, 16 > native16_t; 509 typedef endian< endianness::native, int_least32_t, 24 > native24_t; 510 typedef endian< endianness::native, int_least32_t, 32 > native32_t; 511 typedef endian< endianness::native, int_least64_t, 40 > native40_t; 512 typedef endian< endianness::native, int_least64_t, 48 > native48_t; 513 typedef endian< endianness::native, int_least64_t, 56 > native56_t; 514 typedef endian< endianness::native, int_least64_t, 64 > native64_t; 515 516 // unaligned native endian unsigned integer types 517 typedef endian< endianness::native, uint_least8_t, 8 > unative8_t; 518 typedef endian< endianness::native, uint_least16_t, 16 > unative16_t; 519 typedef endian< endianness::native, uint_least32_t, 24 > unative24_t; 520 typedef endian< endianness::native, uint_least32_t, 32 > unative32_t; 521 typedef endian< endianness::native, uint_least64_t, 40 > unative40_t; 522 typedef endian< endianness::native, uint_least64_t, 48 > unative48_t; 523 typedef endian< endianness::native, uint_least64_t, 56 > unative56_t; 524 typedef endian< endianness::native, uint_least64_t, 64 > unative64_t; 525 526 // These types only present if platform has exact size integers: 527 // aligned big endian signed integer types 528 // aligned big endian unsigned integer types 529 // aligned little endian signed integer types 530 // aligned little endian unsigned integer types 531 532 // aligned native endian typedefs are not provided because 533 // <cstdint> types are superior for this use case 534 535 #ifdef INT16_MAX 536 typedef endian< endianness::big, int16_t, 16, alignment::aligned > aligned_big16_t; 537 typedef endian< endianness::big, uint16_t, 16, alignment::aligned > aligned_ubig16_t; 538 typedef endian< endianness::little, int16_t, 16, alignment::aligned > aligned_little16_t; 539 typedef endian< endianness::little, uint16_t, 16, alignment::aligned > aligned_ulittle16_t; 540 #endif 541 542 #ifdef INT32_MAX 543 typedef endian< endianness::big, int32_t, 32, alignment::aligned > aligned_big32_t; 544 typedef endian< endianness::big, uint32_t, 32, alignment::aligned > aligned_ubig32_t; 545 typedef endian< endianness::little, int32_t, 32, alignment::aligned > aligned_little32_t; 546 typedef endian< endianness::little, uint32_t, 32, alignment::aligned > aligned_ulittle32_t; 547 #endif 548 549 #ifdef INT64_MAX 550 typedef endian< endianness::big, int64_t, 64, alignment::aligned > aligned_big64_t; 551 typedef endian< endianness::big, uint64_t, 64, alignment::aligned > aligned_ubig64_t; 552 typedef endian< endianness::little, int64_t, 64, alignment::aligned > aligned_little64_t; 553 typedef endian< endianness::little, uint64_t, 64, alignment::aligned > aligned_ulittle64_t; 554 #endif 555 556 } // namespace endian 557 }} // namespace boost::spirit 558 559 #undef BOOST_SPIRIT_ENDIAN_DEFAULT_CONSTRUCT 560 #undef BOOST_SPIRIT_ENDIAN_NO_CTORS 561 562 #if defined(__BORLANDC__) || defined( __CODEGEARC__) 563 # pragma pack(pop) 564 #endif 565 566 #endif // BOOST_SPIRIT_ENDIAN_HPP 567