From dff7c1254e0abc6141a4dae26fcb5995a59ca617 Mon Sep 17 00:00:00 2001 From: Beman Date: Sun, 26 May 2013 08:25:10 -0400 Subject: [PATCH] Add unaligned floating point types and supporting infrastructure. --- doc/index.html | 36 +++++---- example/endian_example.cpp | 1 + include/boost/endian/conversion.hpp | 54 +++++++++++++- include/boost/endian/types.hpp | 109 ++++++++++++++++++++++++++++ test/endian_in_union_test.cpp | 2 + test/endian_operations_test.cpp | 2 + test/endian_test.cpp | 80 ++++++++++++++++++++ test/speed_test.cpp | 2 + test/speed_test_functions.cpp | 2 + 9 files changed, 270 insertions(+), 18 deletions(-) diff --git a/doc/index.html b/doc/index.html index ba1a3c7..dab871f 100644 --- a/doc/index.html +++ b/doc/index.html @@ -133,12 +133,12 @@ requirements. Floating point types are not currently supported.

Choosing between endian types and endian conversion functions

-

Which approach is best for dealing with endianness depends on the -application.

+

Which approach is best for dealing with endianness depends on +application concerns.

- + @@ -147,31 +147,37 @@ application.

diff --git a/example/endian_example.cpp b/example/endian_example.cpp index ec8a0e3..674a5ef 100644 --- a/example/endian_example.cpp +++ b/example/endian_example.cpp @@ -10,6 +10,7 @@ //----------------------------------------------------------------------------// #define _CRT_SECURE_NO_DEPRECATE // quiet VC++ 8.0 foolishness +#define _SCL_SECURE_NO_WARNINGS #include diff --git a/include/boost/endian/conversion.hpp b/include/boost/endian/conversion.hpp index 79da437..012d5b4 100644 --- a/include/boost/endian/conversion.hpp +++ b/include/boost/endian/conversion.hpp @@ -126,13 +126,23 @@ namespace endian //----------------------------------- end synopsis -------------------------------------// namespace detail - // This function is unsafe for general use, so is placed in namespace detail. + // These functions are unsafe for general use, so is placed in namespace detail. // Think of what happens if you reverse_value a std::pair; the bytes // from first end up in second and the bytes from second end up in first. Not good! { // general reverse_value function template useful in testing template inline T reverse_value(T x) BOOST_NOEXCEPT; // convert little to big or visa versa + + // conditional unaligned reverse copy, patterned after std::reverse_copy + template + inline void big_reverse_copy(T from, char* to) BOOST_NOEXCEPT; + template + inline void big_reverse_copy(const char* from, T& to) BOOST_NOEXCEPT; + template + inline void little_reverse_copy(T from, char* to) BOOST_NOEXCEPT; + template + inline void little_reverse_copy(const char* from, T& to) BOOST_NOEXCEPT; } //--------------------------------------------------------------------------------------// @@ -237,7 +247,7 @@ namespace endian return *(const double*)&tmp; } - namespace detail // this function is unsafe for general use, so placed in namespace detail + namespace detail { // general reverse_value function template implementation approach using std::reverse // suggested by Mathias Gaunard @@ -250,7 +260,45 @@ namespace endian reinterpret_cast(&tmp) + sizeof(T)); return tmp; } - } + template + inline void big_reverse_copy(T from, char* to) BOOST_NOEXCEPT + { +# ifdef BOOST_BIG_ENDIAN + std::memcpy(to, reinterpret_cast(&from), sizeof(T)); +# else + std::reverse_copy(reinterpret_cast(&from), + reinterpret_cast(&from)+sizeof(T), to); +# endif + } + template + inline void big_reverse_copy(const char* from, T& to) BOOST_NOEXCEPT + { +# ifdef BOOST_BIG_ENDIAN + std::memcpy(reinterpret_cast(&to), from, sizeof(T)); +# else + std::reverse_copy(from, from+sizeof(T), reinterpret_cast(&to)); +# endif + } + template + inline void little_reverse_copy(T from, char* to) BOOST_NOEXCEPT + { +# ifdef BOOST_LITTLE_ENDIAN + std::memcpy(to, reinterpret_cast(&from), sizeof(T)); +# else + std::reverse_copy(reinterpret_cast(&from), + reinterpret_cast(&from)+sizeof(T), to); +# endif + } + template + inline void little_reverse_copy(const char* from, T& to) BOOST_NOEXCEPT + { +# ifdef BOOST_LITTLE_ENDIAN + std::memcpy(reinterpret_cast(&to), from, sizeof(T)); +# else + std::reverse_copy(from, from+sizeof(T), reinterpret_cast(&to)); +# endif + } + } template inline ReversibleValue big_endian_value(ReversibleValue x) BOOST_NOEXCEPT diff --git a/include/boost/endian/types.hpp b/include/boost/endian/types.hpp index 3c9d642..8f6eee0 100644 --- a/include/boost/endian/types.hpp +++ b/include/boost/endian/types.hpp @@ -88,6 +88,14 @@ namespace endian typedef boost::endian::endian little_float32_t; typedef boost::endian::endian little_float64_t; + // unaligned big endian floating point types + typedef boost::endian::endian big_float32un_t; + typedef boost::endian::endian big_float64un_t; + + // unaligned little endian floating point types + typedef boost::endian::endian little_float32un_t; + typedef boost::endian::endian little_float64un_t; + // aligned big endian signed integer types typedef endian big_int16_t; typedef endian big_int32_t; @@ -182,6 +190,7 @@ namespace endian { namespace detail { + // Unrolled loops for loading and storing streams of bytes. template + class endian< order::big, float, 32, align::no > + : cover_operators< endian< order::big, float, 32 >, float > + { + public: + typedef float value_type; +# ifndef BOOST_ENDIAN_NO_CTORS + endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT + explicit endian(value_type val) BOOST_NOEXCEPT + { detail::big_reverse_copy(val, m_value); } +# endif + endian & operator=(value_type val) BOOST_NOEXCEPT + { detail::big_reverse_copy(val, m_value); return *this; } + operator value_type() const BOOST_NOEXCEPT + { + value_type tmp; + detail::big_reverse_copy(m_value, tmp); + return tmp; + } + const char* data() const BOOST_NOEXCEPT { return m_value; } + private: + char m_value[sizeof(value_type)]; + }; + + // unaligned double big endian specialization + template <> + class endian< order::big, double, 64, align::no > + : cover_operators< endian< order::big, double, 64 >, double > + { + public: + typedef double value_type; +# ifndef BOOST_ENDIAN_NO_CTORS + endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT + explicit endian(value_type val) BOOST_NOEXCEPT + { detail::big_reverse_copy(val, m_value); } +# endif + endian & operator=(value_type val) BOOST_NOEXCEPT + { detail::big_reverse_copy(val, m_value); return *this; } + operator value_type() const BOOST_NOEXCEPT + { + value_type tmp; + detail::big_reverse_copy(m_value, tmp); + return tmp; + } + const char* data() const BOOST_NOEXCEPT { return m_value; } + private: + char m_value[sizeof(value_type)]; + }; + + // unaligned float little endian specialization + template <> + class endian< order::little, float, 32, align::no > + : cover_operators< endian< order::little, float, 32 >, float > + { + public: + typedef float value_type; +# ifndef BOOST_ENDIAN_NO_CTORS + endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT + explicit endian(value_type val) BOOST_NOEXCEPT + { detail::little_reverse_copy(val, m_value); } +# endif + endian & operator=(value_type val) BOOST_NOEXCEPT + { detail::little_reverse_copy(val, m_value); return *this; } + operator value_type() const BOOST_NOEXCEPT + { + value_type tmp; + detail::little_reverse_copy(m_value, tmp); + return tmp; + } + const char* data() const BOOST_NOEXCEPT { return m_value; } + private: + char m_value[sizeof(value_type)]; + }; + + // unaligned double little endian specialization + template <> + class endian< order::little, double, 64, align::no > + : cover_operators< endian< order::little, double, 64 >, double > + { + public: + typedef double value_type; +# ifndef BOOST_ENDIAN_NO_CTORS + endian() BOOST_ENDIAN_DEFAULT_CONSTRUCT + explicit endian(value_type val) BOOST_NOEXCEPT + { detail::little_reverse_copy(val, m_value); } +# endif + endian & operator=(value_type val) BOOST_NOEXCEPT + { detail::little_reverse_copy(val, m_value); return *this; } + operator value_type() const BOOST_NOEXCEPT + { + value_type tmp; + detail::little_reverse_copy(m_value, tmp); + return tmp; + } + const char* data() const BOOST_NOEXCEPT { return m_value; } + private: + char m_value[sizeof(value_type)]; + }; // unaligned little endian specialization template diff --git a/test/endian_in_union_test.cpp b/test/endian_in_union_test.cpp index 87e868b..87525c3 100644 --- a/test/endian_in_union_test.cpp +++ b/test/endian_in_union_test.cpp @@ -9,6 +9,8 @@ //----------------------------------------------------------------------------// +#define _SCL_SECURE_NO_WARNINGS + #define BOOST_ENDIAN_FORCE_PODNESS #include diff --git a/test/endian_operations_test.cpp b/test/endian_operations_test.cpp index be04e32..a2be9c0 100644 --- a/test/endian_operations_test.cpp +++ b/test/endian_operations_test.cpp @@ -16,6 +16,8 @@ //----------------------------------------------------------------------------// +#define _SCL_SECURE_NO_WARNINGS + #define BOOST_ENDIAN_LOG #include diff --git a/test/endian_test.cpp b/test/endian_test.cpp index 7307796..a8cb464 100644 --- a/test/endian_test.cpp +++ b/test/endian_test.cpp @@ -16,6 +16,8 @@ //----------------------------------------------------------------------------// +#define _SCL_SECURE_NO_WARNINGS + #include #include @@ -234,6 +236,17 @@ namespace VERIFY(little_float32.data() == reinterpret_cast(&little_float32)); VERIFY(little_float64.data() == reinterpret_cast(&little_float64)); + big_float32un_t big_float32un; + big_float64un_t big_float64un; + little_float32un_t little_float32un; + little_float64un_t little_float64un; + + VERIFY(big_float32un.data() == reinterpret_cast(&big_float32un)); + VERIFY(big_float64un.data() == reinterpret_cast(&big_float64un)); + + VERIFY(little_float32un.data() == reinterpret_cast(&little_float32un)); + VERIFY(little_float64un.data() == reinterpret_cast(&little_float64un)); + VERIFY(big_8.data() == reinterpret_cast(&big_8)); VERIFY(big_16.data() == reinterpret_cast(&big_16)); VERIFY(big_24.data() == reinterpret_cast(&big_24)); @@ -318,6 +331,11 @@ namespace VERIFY_SIZE(sizeof( little_float32_t ), 4 ); VERIFY_SIZE(sizeof( little_float64_t ), 8 ); + VERIFY_SIZE(sizeof( big_float32un_t ), 4 ); + VERIFY_SIZE(sizeof( big_float64un_t ), 8 ); + VERIFY_SIZE(sizeof( little_float32un_t ), 4 ); + VERIFY_SIZE(sizeof( little_float64un_t ), 8 ); + VERIFY_SIZE( sizeof( big_8_t ), 1 ); VERIFY_SIZE( sizeof( big_16_t ), 2 ); VERIFY_SIZE( sizeof( big_24_t ), 3 ); @@ -486,6 +504,18 @@ namespace native_u64_t v31; }; + struct big_float_struct + { + int16_t v0; + big_float32_t v1; + }; + + struct big_unaligned_float_struct + { + int16_t v0; + big_float32un_t v1; + }; + // aligned test cases struct big_aligned_struct @@ -514,6 +544,8 @@ namespace VERIFY_SIZE( sizeof(native_u_struct), 39 ); VERIFY_SIZE( sizeof(big_aligned_struct), 24 ); VERIFY_SIZE( sizeof(little_aligned_struct), 24 ); + VERIFY_SIZE( sizeof(big_float_struct), 8 ); + VERIFY_SIZE( sizeof(big_unaligned_float_struct), 6 ); if ( saved_err_count == err_count ) { @@ -526,6 +558,7 @@ namespace void check_representation_and_range_and_ops() { + // aligned floating point types float big_float32_expected = std::numeric_limits::max(); boost::endian::big_endian(big_float32_expected); big_float32_t big_float32(std::numeric_limits::max()); @@ -560,6 +593,52 @@ namespace VERIFY_VALUE_AND_OPS( little_float64_t, double, std::numeric_limits::max() ); VERIFY_VALUE_AND_OPS( little_float64_t, double, std::numeric_limits::min() ); + // unaligned floating point types + float big_float32un_expected = std::numeric_limits::max(); + boost::endian::big_endian(big_float32un_expected); + big_float32un_t big_float32un(std::numeric_limits::max()); + VERIFY(std::memcmp(big_float32un.data(), + reinterpret_cast(&big_float32un_expected), sizeof(float)) == 0); + + float little_float32un_expected = std::numeric_limits::max(); + boost::endian::little_endian(little_float32un_expected); + little_float32un_t little_float32un(std::numeric_limits::max()); + VERIFY(std::memcmp(little_float32un.data(), + reinterpret_cast(&little_float32un_expected), sizeof(float)) == 0); + + double big_float64un_expected = std::numeric_limits::max(); + boost::endian::big_endian(big_float64un_expected); + big_float64un_t big_float64un(std::numeric_limits::max()); + VERIFY(std::memcmp(big_float64un.data(), + reinterpret_cast(&big_float64un_expected), sizeof(double)) == 0); + + double little_float64un_expected = std::numeric_limits::max(); + boost::endian::little_endian(little_float64un_expected); + little_float64un_t little_float64un(std::numeric_limits::max()); + VERIFY(std::memcmp(little_float64un.data(), + reinterpret_cast(&little_float64un_expected), sizeof(double)) == 0); + + VERIFY_VALUE_AND_OPS( big_float32un_t, float, std::numeric_limits::max() ); + VERIFY_VALUE_AND_OPS( big_float32un_t, float, std::numeric_limits::min() ); + VERIFY_VALUE_AND_OPS( big_float64un_t, double, std::numeric_limits::max() ); + VERIFY_VALUE_AND_OPS( big_float64un_t, double, std::numeric_limits::min() ); + + VERIFY_VALUE_AND_OPS( little_float32un_t, float, std::numeric_limits::max() ); + VERIFY_VALUE_AND_OPS( little_float32un_t, float, std::numeric_limits::min() ); + VERIFY_VALUE_AND_OPS( little_float64un_t, double, std::numeric_limits::max() ); + VERIFY_VALUE_AND_OPS( little_float64un_t, double, std::numeric_limits::min() ); + + float a = 1.0F; + big_float32_t b(1.0F); + big_float32un_t c(1.0F); + little_float32_t d(1.0F); + little_float32un_t e(1.0F); + VERIFY(a==b); + VERIFY(a==c); + VERIFY(a==d); + VERIFY(a==e); + + // unaligned integer types VERIFY_BIG_REPRESENTATION( big_8_t ); VERIFY_VALUE_AND_OPS( big_8_t, int_least8_t, 0x7f ); VERIFY_VALUE_AND_OPS( big_8_t, int_least8_t, -0x80 ); @@ -728,6 +807,7 @@ namespace VERIFY_NATIVE_REPRESENTATION( native_u64_t ); VERIFY_VALUE_AND_OPS( native_u64_t, uint_least64_t, 0xffffffffffffffffULL ); + // aligned integer types VERIFY_BIG_REPRESENTATION( big_int16_t ); VERIFY_VALUE_AND_OPS( big_int16_t, int_least16_t, 0x7fff ); VERIFY_VALUE_AND_OPS( big_int16_t, int_least16_t, -0x8000 ); diff --git a/test/speed_test.cpp b/test/speed_test.cpp index b07e40a..36eeaae 100644 --- a/test/speed_test.cpp +++ b/test/speed_test.cpp @@ -7,6 +7,8 @@ //--------------------------------------------------------------------------------------// +#define _SCL_SECURE_NO_WARNINGS + #define BOOST_ENDIAN_NO_INTRINSICS //#define BOOST_ENDIAN_LOG diff --git a/test/speed_test_functions.cpp b/test/speed_test_functions.cpp index 97f466e..44548a3 100644 --- a/test/speed_test_functions.cpp +++ b/test/speed_test_functions.cpp @@ -13,6 +13,8 @@ //--------------------------------------------------------------------------------------// +#define _SCL_SECURE_NO_WARNINGS + #include "speed_test_functions.hpp" namespace user
AdvantagesNeeds that favor one approach over the other
Endian types
    -
  • Mimic the built-in types. This can simplify use and eliminate logic - errors since there is no need to reason about the current endianness of - variables.
    +
  • A need to simplify program logic and eliminate logic + errors. Since the endian types mimic the built-in types, there is no need to reason about the current endianness of variables + and that can simplify program logic and eliminate logic errors.
     
  • -
  • 1, 2, 3, 4, 5, 6, 7, and 8 byte integers are supported. Use of 3, 5, - 6, or 7 byte integers can reduce internal and external space usage and +
  • A need to use unusual integer sizes (i.e. 3, 5, + 6, or 7 bytes) to reduce internal and external space usage and save I/O time.
     
  • -
  • Alignment is not required. This can eliminate padding bytes in +
  • A need to use unaligned variables. Endian types can eliminate padding bytes in structures, reducing internal and external space usage and saving I/O - time.
  • + time. They can deals with structures defined like this:
+
+

struct S {
+   uint16_t a;
+   uint32_t b;
+ } __attribute__ ((packed));

+
    -
  • Already familiar to developers who have been using C conversion +
  • A need to leverage knowledge of developers who have been using C byte + swapping functions for years.
     
  • -
  • Uses less CPU time, particularly if each variable is used many times - relative to I/O of the variable.
    +
  • A need to save CPU time when a variable is used many times + relative to its I/O.
     
  • -
  • Easier to pass structures to third-party libraries expecting a +
  • A need to pass structures to third-party libraries expecting a specific structure format.
     
  • -
  • Supports float and double.