diff --git a/doc/done_list.html b/doc/done_list.html new file mode 100644 index 0000000..28f35a4 --- /dev/null +++ b/doc/done_list.html @@ -0,0 +1,44 @@ + + + + + + + +New Page 1 + + + + +

Endian changes since formal review

+ + + + + diff --git a/doc/index.html b/doc/index.html index 4172d1b..bd192a5 100644 --- a/doc/index.html +++ b/doc/index.html @@ -178,11 +178,14 @@ application.

Overall FAQ

-

Why bother with endianness? Does endianness have any uses outside of -portable binary file or network I/O formats?

+

Why bother with endianness?

-

Binary data portability is the primary use case, and that implies I/O.

-

Using the 3, 5, 6, and 7 byte integer types to save internal or external +

Binary data portability is the primary use case.

+
+

Does endianness have any uses outside of portable binary file or network +I/O formats?

+
+

Using the unaligned integer types to save internal or external memory space is a minor secondary use case.

Why bother with binary I/O? Why not just use C++ Standard Library stream @@ -198,6 +201,13 @@ files, limit usefulness to applications where the binary I/O advantages are paramount.

+

Why is only big, little, and native endianness supported?

+
+

These are the only endian schemes that have any practical value today. PDP-11 +and the other middle endian approaches are interesting historical curiosities +but have no relevance to C++ developers.

+
+

Acknowledgements

Comments and suggestions were received from @@ -216,7 +226,7 @@ Tim Blechmann, Tim Moore, tymofey, Tomas Puverle, Vincente Botet, Yuval Ronen and Vitaly Budovski,.


Last revised: -20 May, 2013

+22 May, 2013

© Copyright Beman Dawes, 2011, 2013

Distributed under the Boost Software License, Version 1.0. See www.boost.org/ LICENSE_1_0.txt

diff --git a/doc/types.html b/doc/types.html index 5e5dbdc..50c43c8 100644 --- a/doc/types.html +++ b/doc/types.html @@ -109,64 +109,69 @@ using namespace boost::endian; namespace { - // This is an extract from a very widely used GIS file format. I have no idea - // why a designer would mix big and little endians in the same file - but - // this is a real-world format and users wishing to write low level code - // manipulating these files have to deal with the mixed endianness. + // This is an extract from a very widely used GIS file format. Who knows + // why a designer would mix big and little endians in the same file - but + // this is a real-world format and users wishing to write low level code + // manipulating these files have to deal with the mixed endianness. struct header { - big32_t file_code; - big32_t file_length; - little32_t version; - little32_t shape_type; + big_int32_t file_code; + big_int32_t file_length; + little_int32_t version; + little_int32_t shape_type; }; - const char * filename = "test.dat"; + const char* filename = "test.dat"; } -int main() +int main(int, char* []) { - BOOST_STATIC_ASSERT( sizeof( header ) == 16U ); // check requirement - + BOOST_STATIC_ASSERT(sizeof(header) == 16U); // reality check + header h; h.file_code = 0x01020304; - h.file_length = sizeof( header ); - h.version = -1; + h.file_length = sizeof(header); + h.version = 1; h.shape_type = 0x01020304; - // Low-level I/O such as POSIX read/write or <cstdio> fread/fwrite is sometimes - // used for binary file operations when ultimate efficiency is important. - // Such I/O is often performed in some C++ wrapper class, but to drive home the - // point that endian integers are often used in fairly low-level code that - // does bulk I/O operations, <cstdio> fopen/fwrite is used for I/O in this example. + // Low-level I/O such as POSIX read/write or <cstdio> fread/fwrite is sometimes + // used for binary file operations when ultimate efficiency is important. + // Such I/O is often performed in some C++ wrapper class, but to drive home the + // point that endian integers are often used in fairly low-level code that + // does bulk I/O operations, <cstdio> fopen/fwrite is used for I/O in this example. - std::FILE * fi; - - if ( !(fi = std::fopen( filename, "wb" )) ) // MUST BE BINARY + std::FILE* fi = std::fopen(filename, "wb"); // MUST BE BINARY + + if (!fi) { std::cout << "could not open " << filename << '\n'; return 1; } - if ( std::fwrite( &h, sizeof( header ), 1, fi ) != 1 ) + if (std::fwrite(&h, sizeof(header), 1, fi)!= 1) { std::cout << "write failure for " << filename << '\n'; return 1; } - std::fclose( fi ); + std::fclose(fi); std::cout << "created file " << filename << '\n'; + return 0; -} +} +

After compiling and executing endian_example.cpp, a hex dump of test.dat shows:

-
0102 0304 0000 0010 ffff ffff 0403 0201
+
01020304 00000010 01000000 04030201
+

Notice that the first two 32-bit integers are big endian while the second two +are little endian, even though the machine this was compiled and run on was +little endian.

Limitations

Requires <climits> CHAR_BIT == 8. If CHAR_BIT is some other value, compilation will result in an #error. This @@ -178,7 +183,7 @@ because it has constructors, private data members, and a base class. This means that common use cases are relying on unspecified behavior in that the C++ Standard does not guarantee memory layout for non-POD types. This has not been a problem in practice since all known C++ compilers do layout memory as if -endian were a POD type. In C++11, it will be possible to specify the +endian were a POD type. In C++11, it is possible to specify the default constructor as trivial, and private data members and base classes will no longer disqualify a type from being a POD. Thus under C++11, endian will no longer be relying on unspecified behavior.

@@ -190,15 +195,19 @@ will no longer be relying on unspecified behavior.

  • 1-8 byte (unaligned) | 2, 4, 8 byte (aligned)
  • Choice of integer value type
  • -

    Typedefs

    +

    Enums and typedefs

    +

    Two scoped enums are provided:

    +
    +
    enum class order {big, little, native};
    +enum class align {no, yes}; 
    +

    One class template is provided:

    -
    template <endianness::enum_t E, typename T, std::size_t n_bytes,
    -  alignment::enum_t A = alignment::unaligned>
    -class endian;
    +  
    template <order Order, typename T, std::size_t n_bits, align A = align::no>
    +  class endian;
     
    -

    Sixty typedefs, such as big32_t, provide convenient naming +

    Typedefs, such as big_int32_t, provide convenient naming conventions for common use cases:

    @@ -210,76 +219,76 @@ conventions for common use cases:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - + - - - + + + - + - - - + + + - + - - - + + + - + -
    Alignment
    bign_tbigsigned8,16,24,32,40,48,56,64unaligned
    ubign_tbigunsigned8,16,24,32,40,48,56,64unaligned
    littlen_tlittlesigned8,16,24,32,40,48,56,64unaligned
    ulittlen_tlittleunsigned8,16,24,32,40,48,56,64unaligned
    nativen_tnativesigned8,16,24,32,40,48,56,64unaligned
    unativen_tnativeunsigned8,16,24,32,40,48,56,64unaligned
    aligned_bign_tbigsignedbig_intn_tbigsigned 16,32,64alignedyes
    aligned_ubign_tbigunsignedbig_uintn_tbigunsigned 16,32,64alignedyes
    aligned_littlen_tlittlesignedlittle_intn_tlittlesigned 16,32,64alignedyes
    aligned_ulittlen_tlittleunsignedlittle_uintn_tlittleunsigned 16,32,64alignedyes
    + + big_n_t + big + signed + 8,16,24,32,40,48,56,64 + no + + + big_un_t + big + unsigned + 8,16,24,32,40,48,56,64 + no + + + little_n_t + little + signed + 8,16,24,32,40,48,56,64 + no + + + little_un_t + little + unsigned + 8,16,24,32,40,48,56,64 + no + + + native_n_t + native + signed + 8,16,24,32,40,48,56,64 + no + + + native_un_t + native + unsigned + 8,16,24,32,40,48,56,64 + no + +

    The unaligned types do not cause compilers to insert padding bytes in classes and structs. This is an important characteristic that can be exploited to minimize wasted space in @@ -289,8 +298,9 @@ Code that uses aligned types is inherently non-portable because alignment requirements vary between hardware architectures and because alignment may be affected by compiler switches or pragmas. Furthermore, aligned types are only available on architectures with 16, 32, and 64-bit integer types.

    -

    Note: One-byte big-endian, little-endian, and native-endian types provide identical -functionality. All three names are provided to improve code readability and searchability.

    +

    Note: One-byte big-endian, little-endian, and native-endian types +have identical +functionality. They are provided to improve code readability and searchability.

    Comment on naming

    When first exposed to endian types, programmers often fit them into a mental model based on the <cstdint> types. Using that model, it is natural to @@ -593,7 +603,7 @@ sign partial specialization to correctly extend the sign when cover integer size differs from endian representation size.


    Last revised: -21 May, 2013

    +22 May, 2013

    © Copyright Beman Dawes, 2006-2009

    Distributed under the Boost Software License, Version 1.0. See www.boost.org/ LICENSE_1_0.txt

    diff --git a/example/endian_example.cpp b/example/endian_example.cpp index 9e6f9d3..883db8a 100644 --- a/example/endian_example.cpp +++ b/example/endian_example.cpp @@ -22,7 +22,7 @@ using namespace boost::endian; namespace { - // This is an extract from a very widely used GIS file format. I have no idea + // This is an extract from a very widely used GIS file format. Who knows // why a designer would mix big and little endians in the same file - but // this is a real-world format and users wishing to write low level code // manipulating these files have to deal with the mixed endianness. @@ -35,18 +35,18 @@ namespace little_int32_t shape_type; }; - const char * filename = "test.dat"; + const char* filename = "test.dat"; } -int main(int, char * []) +int main(int, char* []) { - BOOST_STATIC_ASSERT( sizeof( header ) == 16U ); // check requirement + BOOST_STATIC_ASSERT(sizeof(header) == 16U); // reality check header h; h.file_code = 0x01020304; - h.file_length = sizeof( header ); - h.version = -1; + h.file_length = sizeof(header); + h.version = 1; h.shape_type = 0x01020304; // Low-level I/O such as POSIX read/write or fread/fwrite is sometimes @@ -55,21 +55,21 @@ int main(int, char * []) // point that endian integers are often used in fairly low-level code that // does bulk I/O operations, fopen/fwrite is used for I/O in this example. - std::FILE * fi = std::fopen( filename, "wb" ); // MUST BE BINARY + std::FILE* fi = std::fopen(filename, "wb"); // MUST BE BINARY - if ( !fi ) + if (!fi) { std::cout << "could not open " << filename << '\n'; return 1; } - if ( std::fwrite( &h, sizeof( header ), 1, fi ) != 1 ) + if (std::fwrite(&h, sizeof(header), 1, fi)!= 1) { std::cout << "write failure for " << filename << '\n'; return 1; } - std::fclose( fi ); + std::fclose(fi); std::cout << "created file " << filename << '\n'; diff --git a/include/boost/endian/conversion.hpp b/include/boost/endian/conversion.hpp index 2070d4a..79da437 100644 --- a/include/boost/endian/conversion.hpp +++ b/include/boost/endian/conversion.hpp @@ -23,7 +23,7 @@ namespace boost namespace endian { #ifndef BOOST_ENDIAN_ORDER_ENUM_DEFINED - BOOST_SCOPED_ENUM_START(order) { big, little, native }; BOOST_SCOPED_ENUM_END + BOOST_SCOPED_ENUM_START(order) {big, little, native}; BOOST_SCOPED_ENUM_END # define BOOST_ENDIAN_ORDER_ENUM_DEFINED #endif diff --git a/include/boost/endian/detail/cover_operators.hpp b/include/boost/endian/detail/cover_operators.hpp index 6aa8c51..e0abd3c 100644 --- a/include/boost/endian/detail/cover_operators.hpp +++ b/include/boost/endian/detail/cover_operators.hpp @@ -28,6 +28,7 @@ # include # endif +#include #include namespace boost @@ -47,46 +48,46 @@ namespace boost // built into unary +. // Unary operations. - friend IntegerType operator+(const T& x) { return x; } + friend IntegerType operator+(const T& x) BOOST_NOEXCEPT { return x; } # ifndef BOOST_MINIMAL_INTEGER_COVER_OPERATORS - friend IntegerType operator-(const T& x) { return -+x; } - friend IntegerType operator~(const T& x) { return ~+x; } - friend IntegerType operator!(const T& x) { return !+x; } + friend IntegerType operator-(const T& x) BOOST_NOEXCEPT { return -+x; } + friend IntegerType operator~(const T& x) BOOST_NOEXCEPT { return ~+x; } + friend IntegerType operator!(const T& x) BOOST_NOEXCEPT { return !+x; } // The basic ordering operations. - friend bool operator==(const T& x, IntegerType y) { return +x == y; } - friend bool operator<(const T& x, IntegerType y) { return +x < y; } + friend bool operator==(const T& x, IntegerType y) BOOST_NOEXCEPT { return +x == y; } + friend bool operator<(const T& x, IntegerType y) BOOST_NOEXCEPT { return +x < y; } # endif // The basic arithmetic operations. - friend T& operator+=(T& x, IntegerType y) { return x = +x + y; } - friend T& operator-=(T& x, IntegerType y) { return x = +x - y; } - friend T& operator*=(T& x, IntegerType y) { return x = +x * y; } - friend T& operator/=(T& x, IntegerType y) { return x = +x / y; } - friend T& operator%=(T& x, IntegerType y) { return x = +x % y; } - friend T& operator&=(T& x, IntegerType y) { return x = +x & y; } - friend T& operator|=(T& x, IntegerType y) { return x = +x | y; } - friend T& operator^=(T& x, IntegerType y) { return x = +x ^ y; } - friend T& operator<<=(T& x, IntegerType y) { return x = +x << y; } - friend T& operator>>=(T& x, IntegerType y) { return x = +x >> y; } + friend T& operator+=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x + y; } + friend T& operator-=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x - y; } + friend T& operator*=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x * y; } + friend T& operator/=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x / y; } + friend T& operator%=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x % y; } + friend T& operator&=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x & y; } + friend T& operator|=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x | y; } + friend T& operator^=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x ^ y; } + friend T& operator<<=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x << y; } + friend T& operator>>=(T& x, IntegerType y) BOOST_NOEXCEPT { return x = +x >> y; } // A few binary arithmetic operations not covered by operators base class. - friend IntegerType operator<<(const T& x, IntegerType y) { return +x << y; } - friend IntegerType operator>>(const T& x, IntegerType y) { return +x >> y; } + friend IntegerType operator<<(const T& x, IntegerType y) BOOST_NOEXCEPT { return +x << y; } + friend IntegerType operator>>(const T& x, IntegerType y) BOOST_NOEXCEPT { return +x >> y; } // Auto-increment and auto-decrement can be defined in terms of the // arithmetic operations. - friend T& operator++(T& x) { return x += 1; } - friend T& operator--(T& x) { return x -= 1; } + friend T& operator++(T& x) BOOST_NOEXCEPT { return x += 1; } + friend T& operator--(T& x) BOOST_NOEXCEPT { return x -= 1; } # ifdef BOOST_MINIMAL_INTEGER_COVER_OPERATORS - friend T operator++(T& x, int) + friend T operator++(T& x, int) BOOST_NOEXCEPT { T tmp(x); x += 1; return tmp; } - friend T operator--(T& x, int) + friend T operator--(T& x, int) BOOST_NOEXCEPT { T tmp(x); x -= 1; diff --git a/include/boost/endian/types.hpp b/include/boost/endian/types.hpp index 7ba6886..bdbd1c2 100644 --- a/include/boost/endian/types.hpp +++ b/include/boost/endian/types.hpp @@ -182,17 +182,17 @@ namespace endian { typedef unrolled_byte_loops next; - static T load_big(const unsigned char* bytes) + static T load_big(const unsigned char* bytes) BOOST_NOEXCEPT { return *(bytes - 1) | (next::load_big(bytes - 1) << 8); } - static T load_little(const unsigned char* bytes) + static T load_little(const unsigned char* bytes) BOOST_NOEXCEPT { return *bytes | (next::load_little(bytes + 1) << 8); } - static void store_big(char* bytes, T value) + static void store_big(char* bytes, T value) BOOST_NOEXCEPT { *(bytes - 1) = static_cast(value); next::store_big(bytes - 1, value >> 8); } - static void store_little(char* bytes, T value) + static void store_little(char* bytes, T value) BOOST_NOEXCEPT { *bytes = static_cast(value); next::store_little(bytes + 1, value >> 8); @@ -202,13 +202,13 @@ namespace endian template struct unrolled_byte_loops { - static T load_big(const unsigned char* bytes) + static T load_big(const unsigned char* bytes) BOOST_NOEXCEPT { return *(bytes - 1); } - static T load_little(const unsigned char* bytes) + static T load_little(const unsigned char* bytes) BOOST_NOEXCEPT { return *bytes; } - static void store_big(char* bytes, T value) + static void store_big(char* bytes, T value) BOOST_NOEXCEPT { *(bytes - 1) = static_cast(value); } - static void store_little(char* bytes, T value) + static void store_little(char* bytes, T value) BOOST_NOEXCEPT { *bytes = static_cast(value); } }; @@ -216,19 +216,19 @@ namespace endian template struct unrolled_byte_loops { - static T load_big(const unsigned char* bytes) + static T load_big(const unsigned char* bytes) BOOST_NOEXCEPT { return *reinterpret_cast(bytes - 1); } - static T load_little(const unsigned char* bytes) + static T load_little(const unsigned char* bytes) BOOST_NOEXCEPT { return *reinterpret_cast(bytes); } - static void store_big(char* bytes, T value) + static void store_big(char* bytes, T value) BOOST_NOEXCEPT { *(bytes - 1) = static_cast(value); } - static void store_little(char* bytes, T value) + static void store_little(char* bytes, T value) BOOST_NOEXCEPT { *bytes = static_cast(value); } }; template inline - T load_big_endian(const void* bytes) + T load_big_endian(const void* bytes) BOOST_NOEXCEPT { return unrolled_byte_loops::load_big (static_cast(bytes) + n_bytes); @@ -236,7 +236,7 @@ namespace endian template inline - T load_little_endian(const void* bytes) + T load_little_endian(const void* bytes) BOOST_NOEXCEPT { return unrolled_byte_loops::load_little (static_cast(bytes)); @@ -244,7 +244,7 @@ namespace endian template inline - void store_big_endian(void* bytes, T value) + void store_big_endian(void* bytes, T value) BOOST_NOEXCEPT { unrolled_byte_loops::store_big (static_cast(bytes) + n_bytes, value); @@ -252,7 +252,7 @@ namespace endian template inline - void store_little_endian(void* bytes, T value) + void store_little_endian(void* bytes, T value) BOOST_NOEXCEPT { unrolled_byte_loops::store_little (static_cast(bytes), value); @@ -281,7 +281,7 @@ namespace endian typedef T value_type; # ifndef BOOST_ENDIAN_NO_CTORS endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT - explicit endian(T val) + explicit endian(T val) BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -290,8 +290,9 @@ namespace endian detail::store_big_endian(m_value, val); } # endif - endian & operator=(T val) { detail::store_big_endian(m_value, val); return *this; } - operator T() const + endian & operator=(T val) BOOST_NOEXCEPT + { detail::store_big_endian(m_value, val); return *this; } + operator T() const BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -299,7 +300,7 @@ namespace endian # endif return detail::load_big_endian(m_value); } - const char* data() const { return m_value; } + const char* data() const BOOST_NOEXCEPT { return m_value; } private: char m_value[n_bits/8]; }; @@ -314,7 +315,7 @@ namespace endian typedef T value_type; # ifndef BOOST_ENDIAN_NO_CTORS endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT - explicit endian(T val) + explicit endian(T val) BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -323,8 +324,9 @@ namespace endian detail::store_little_endian(m_value, val); } # endif - endian & operator=(T val) { detail::store_little_endian(m_value, val); return *this; } - operator T() const + endian & operator=(T val) BOOST_NOEXCEPT + { detail::store_little_endian(m_value, val); return *this; } + operator T() const BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -332,7 +334,7 @@ namespace endian # endif return detail::load_little_endian(m_value); } - const char* data() const { return m_value; } + const char* data() const BOOST_NOEXCEPT { return m_value; } private: char m_value[n_bits/8]; }; @@ -348,19 +350,23 @@ namespace endian # ifndef BOOST_ENDIAN_NO_CTORS endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT # ifdef BOOST_BIG_ENDIAN - explicit endian(T val) { detail::store_big_endian(m_value, val); } + explicit endian(T val) BOOST_NOEXCEPT { detail::store_big_endian(m_value, val); } # else - explicit endian(T val) { detail::store_little_endian(m_value, val); } + explicit endian(T val) BOOST_NOEXCEPT { detail::store_little_endian(m_value, val); } # endif # endif # ifdef BOOST_BIG_ENDIAN - endian & operator=(T val) { detail::store_big_endian(m_value, val); return *this; } - operator T() const { return detail::load_big_endian(m_value); } + endian & operator=(T val) BOOST_NOEXCEPT + { detail::store_big_endian(m_value, val); return *this; } + operator T() const BOOST_NOEXCEPT + { return detail::load_big_endian(m_value); } # else - endian & operator=(T val) { detail::store_little_endian(m_value, val); return *this; } - operator T() const { return detail::load_little_endian(m_value); } + endian & operator=(T val) BOOST_NOEXCEPT + { detail::store_little_endian(m_value, val); return *this; } + operator T() const BOOST_NOEXCEPT + { return detail::load_little_endian(m_value); } # endif - const char* data() const { return m_value; } + const char* data() const BOOST_NOEXCEPT { return m_value; } private: char m_value[n_bits/8]; }; @@ -378,7 +384,7 @@ namespace endian typedef T value_type; # ifndef BOOST_ENDIAN_NO_CTORS endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT - explicit endian(T val) + explicit endian(T val) BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -388,12 +394,12 @@ namespace endian } # endif - endian& operator=(T val) + endian& operator=(T val) BOOST_NOEXCEPT { m_value = ::boost::endian::big_endian_value(val); return *this; } - operator T() const + operator T() const BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -401,7 +407,7 @@ namespace endian # endif return ::boost::endian::big_endian_value(m_value); } - const char* data() const {return reinterpret_cast(&m_value);} + const char* data() const BOOST_NOEXCEPT {return reinterpret_cast(&m_value);} private: T m_value; }; @@ -417,7 +423,7 @@ namespace endian typedef T value_type; # ifndef BOOST_ENDIAN_NO_CTORS endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT - explicit endian(T val) + explicit endian(T val) BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -427,12 +433,12 @@ namespace endian } # endif - endian& operator=(T val) + endian& operator=(T val) BOOST_NOEXCEPT { m_value = ::boost::endian::little_endian_value(val); return *this; } - operator T() const + operator T() const BOOST_NOEXCEPT { # ifdef BOOST_ENDIAN_LOG if ( endian_log ) @@ -440,7 +446,7 @@ namespace endian # endif return ::boost::endian::little_endian_value(m_value); } - const char* data() const {return reinterpret_cast(&m_value);} + const char* data() const BOOST_NOEXCEPT {return reinterpret_cast(&m_value);} private: T m_value; };