Work in progress. Detect wrong endianness reported by boost/detail/endian.hpp. Add intrinsic timing to benchmark.cpp. Add boost/endian/detail/intrinsic.hpp. Use intrinsics in converters when available. Begin adding convert_bytes tests to converter_test.cpp.

This commit is contained in:
Beman
2013-05-10 16:51:24 -04:00
parent fcb5386537
commit 553d6a2426
7 changed files with 183 additions and 34 deletions

View File

@@ -1,4 +1,4 @@
// boost/endian/conversion.hpp -------------------------------------------------------//
// boost/endian/converters.hpp -------------------------------------------------------//
// Copyright Beman Dawes 2010, 2011
@@ -8,9 +8,10 @@
#ifndef BOOST_ENDIAN_CONVERTERS_HPP
#define BOOST_ENDIAN_CONVERTERS_HPP
#include "boost/config.hpp"
#include <boost/config.hpp>
#include <boost/detail/endian.hpp>
#include <boost/cstdint.hpp>
#include <boost/endian/detail/intrinsic.hpp>
#include <boost/detail/scoped_enum_emulation.hpp>
#include <algorithm>
@@ -62,7 +63,7 @@ namespace endian
// compile-time generic byte order conversion
template <BOOST_SCOPED_ENUM(order) From, BOOST_SCOPED_ENUM(order) To, class T>
inline T convert_bytes(T from) BOOST_NOEXCEPT;
/*inline*/ T convert_bytes(T from) BOOST_NOEXCEPT;
// runtime actual byte-order determination
inline BOOST_SCOPED_ENUM(order) actual_order(BOOST_SCOPED_ENUM(order) o) BOOST_NOEXCEPT;
@@ -74,58 +75,84 @@ namespace endian
BOOST_SCOPED_ENUM(order) to_order) BOOST_NOEXCEPT;
//----------------------------------- implementation -----------------------------------//
// -- reverse_bytes implementation approach suggested by tymofey, with avoidance of
// -- reverse_bytes portable approach suggested by tymofey, with avoidance of
// undefined behavior as suggested by Giovanni Piero Deretta, and a further
// refinement suggested by Pyry Jahkola.
// -- reverse_bytes intrinsic approach suggested by David Stone, who privided his
// Boost licensed macro implementation.
inline int16_t reverse_bytes(int16_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
return (static_cast<uint16_t>(x) << 8)
| (static_cast<uint16_t>(x) >> 8);
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_2(static_cast<uint16_t>(x));
# endif
}
inline int32_t reverse_bytes(int32_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
uint32_t step16;
step16 = static_cast<uint32_t>(x) << 16 | static_cast<uint32_t>(x) >> 16;
return
((static_cast<uint32_t>(step16) << 8) & 0xff00ff00)
| ((static_cast<uint32_t>(step16) >> 8) & 0x00ff00ff);
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4(static_cast<uint32_t>(x));
# endif
}
inline int64_t reverse_bytes(int64_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
uint64_t step32, step16;
step32 = static_cast<uint64_t>(x) << 32 | static_cast<uint64_t>(x) >> 32;
step16 = (step32 & 0x0000FFFF0000FFFF) << 16
| (step32 & 0xFFFF0000FFFF0000) >> 16;
return static_cast<int64_t>((step16 & 0x00FF00FF00FF00FF) << 8
| (step16 & 0xFF00FF00FF00FF00) >> 8);
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_8(static_cast<uint64_t>(x));
# endif
}
inline uint16_t reverse_bytes(uint16_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
return (x << 8)
| (x >> 8);
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_2(x);
# endif
}
inline uint32_t reverse_bytes(uint32_t x) BOOST_NOEXCEPT
inline uint32_t reverse_bytes(uint32_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
uint32_t step16;
step16 = x << 16 | x >> 16;
return
((step16 << 8) & 0xff00ff00)
| ((step16 >> 8) & 0x00ff00ff);
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4(x);
# endif
}
inline uint64_t reverse_bytes(uint64_t x) BOOST_NOEXCEPT
{
# ifdef BOOST_ENDIAN_NO_INTRINSICS
uint64_t step32, step16;
step32 = x << 32 | x >> 32;
step16 = (step32 & 0x0000FFFF0000FFFF) << 16
| (step32 & 0xFFFF0000FFFF0000) >> 16;
return (step16 & 0x00FF00FF00FF00FF) << 8
| (step16 & 0xFF00FF00FF00FF00) >> 8;
# else
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_8(x);
# endif
}
// general reverse_bytes function template implementation approach using std::reverse
@@ -161,6 +188,62 @@ namespace endian
# endif
}
template<> inline int16_t convert_bytes<order::big, order::big>(int16_t x) BOOST_NOEXCEPT {return x;}
template<> inline int16_t convert_bytes<order::little, order::little>(int16_t x) BOOST_NOEXCEPT {return x;}
template<> inline int16_t convert_bytes<order::native, order::native>(int16_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint16_t convert_bytes<order::big, order::big>(uint16_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint16_t convert_bytes<order::little, order::little>(uint16_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint16_t convert_bytes<order::native, order::native>(uint16_t x) BOOST_NOEXCEPT {return x;}
template<> inline int32_t convert_bytes<order::big, order::big>(int32_t x) BOOST_NOEXCEPT {return x;}
template<> inline int32_t convert_bytes<order::little, order::little>(int32_t x) BOOST_NOEXCEPT {return x;}
template<> inline int32_t convert_bytes<order::native, order::native>(int32_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint32_t convert_bytes<order::big, order::big>(uint32_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint32_t convert_bytes<order::little, order::little>(uint32_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint32_t convert_bytes<order::native, order::native>(uint32_t x) BOOST_NOEXCEPT {return x;}
template<> inline int64_t convert_bytes<order::big, order::big>(int64_t x) BOOST_NOEXCEPT {return x;}
template<> inline int64_t convert_bytes<order::little, order::little>(int64_t x) BOOST_NOEXCEPT {return x;}
template<> inline int64_t convert_bytes<order::native, order::native>(int64_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint64_t convert_bytes<order::big, order::big>(uint64_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint64_t convert_bytes<order::little, order::little>(uint64_t x) BOOST_NOEXCEPT {return x;}
template<> inline uint64_t convert_bytes<order::native, order::native>(uint64_t x) BOOST_NOEXCEPT {return x;}
template<> inline int16_t convert_bytes<order::big, order::little>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int16_t convert_bytes<order::big, order::native>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int16_t convert_bytes<order::little, order::big>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int16_t convert_bytes<order::little, order::native>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int16_t convert_bytes<order::native, order::big>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int16_t convert_bytes<order::native, order::little>(int16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::big, order::little>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::big, order::native>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::little, order::big>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::little, order::native>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::native, order::big>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint16_t convert_bytes<order::native, order::little>(uint16_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::big, order::little>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::big, order::native>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::little, order::big>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::little, order::native>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::native, order::big>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int32_t convert_bytes<order::native, order::little>(int32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::big, order::little>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::big, order::native>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::little, order::big>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::little, order::native>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::native, order::big>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint32_t convert_bytes<order::native, order::little>(uint32_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::big, order::little>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::big, order::native>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::little, order::big>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::little, order::native>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::native, order::big>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline int64_t convert_bytes<order::native, order::little>(int64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::big, order::little>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::big, order::native>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::little, order::big>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::little, order::native>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::native, order::big>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
template<> inline uint64_t convert_bytes<order::native, order::little>(uint64_t x) BOOST_NOEXCEPT {return reverse_bytes(x);}
inline BOOST_SCOPED_ENUM(order) actual_order(BOOST_SCOPED_ENUM(order) o) BOOST_NOEXCEPT
{
return o != order::native ? o :

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2012 David Stone
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://www.boost.org/libs/endian/ for documentation.
#ifndef BOOST_ENDIAN_INTRINSIC_HPP
#define BOOST_ENDIAN_INTRINSIC_HPP
#ifndef BOOST_DETAIL_ENDIAN_HPP
# include <boost/detail/endian.hpp>
#endif
#ifndef BOOST_CSTDINT_HPP
# include <boost/cstdint.hpp>
#endif
#undef BOOST_ENDIAN_NO_INTRINSICS
#undef BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_2
#undef BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4
#undef BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_8
#if (defined __GNUC__ || defined __clang__)
#include <byteswap.h>
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_2 bswap_16
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4 bswap_32
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_8 bswap_64
#elif defined _MSC_VER
#include <cstdlib>
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_2 _byteswap_ushort
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4 _byteswap_ulong
#define BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_8 _byteswap_uint64
#else
#define BOOST_ENDIAN_NO_INTRINSICS
#endif
#endif BOOST_ENDIAN_INTRINSIC_HPP

View File

@@ -35,6 +35,7 @@ namespace
#endif
typedef boost::timer::nanosecond_type nanosecond_t;
nanosecond_t benchmark(timee_func timee, const char* msg,
nanosecond_t overhead = 0)
{
@@ -58,7 +59,7 @@ namespace
t.stop();
times = t.elapsed();
cpu_time = (times.system + times.user) - overhead;
const long double sec = 1000000.0L;
const long double sec = 1000000000.0L;
cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
cout.precision(places);
cout << msg << " " << cpu_time / sec << endl;
@@ -131,6 +132,11 @@ namespace
| (static_cast<uint32_t>(x) >> 24);
}
inline int32_t by_return_intrinsic(int32_t x)
{
return BOOST_ENDIAN_INTRINSIC_BYTE_SWAP_4(static_cast<uint32_t>(x));
}
inline int32_t by_return_pyry(int32_t x)
{
uint32_t step16;
@@ -146,42 +152,48 @@ namespace
| ((x >> 8) & 0x0000ff00);
}
int32_t modify_noop(int32_t x)
inline int32_t modify_noop(int32_t x)
{
int32_t v(x);
return v;
}
int32_t modify_in_place(int32_t x)
inline int32_t modify_in_place(int32_t x)
{
int32_t v(x);
in_place(v);
return v;
}
int32_t modify_by_return(int32_t x)
inline int32_t modify_by_return(int32_t x)
{
int32_t v(x);
return by_return(v);
}
int32_t modify_by_return_pyry(int32_t x)
inline int32_t modify_by_return_pyry(int32_t x)
{
int32_t v(x);
return by_return_pyry(v);
}
void non_modify_assign(int32_t x, int32_t& y)
inline int32_t modify_by_return_intrinsic(int32_t x)
{
int32_t v(x);
return by_return_intrinsic(v);
}
inline void non_modify_assign(int32_t x, int32_t& y)
{
y = x;
}
void non_modify_two_operand(int32_t x, int32_t& y)
inline void non_modify_two_operand(int32_t x, int32_t& y)
{
two_operand(x, y);
}
void non_modify_by_return(int32_t x, int32_t& y)
inline void non_modify_by_return(int32_t x, int32_t& y)
{
y = by_return(x);
}
@@ -198,9 +210,10 @@ int main(int argc, char * argv[])
#ifndef BOOST_TWO_ARG
overhead = benchmark(modify_noop, "modify no-op");
benchmark(modify_in_place, "modify in place", overhead);
benchmark(modify_by_return, "modify by return", overhead);
benchmark(modify_by_return_pyry, "modify by return", overhead);
benchmark(modify_in_place, "modify in place"/*, overhead*/);
benchmark(modify_by_return, "modify by return"/*, overhead*/);
benchmark(modify_by_return_pyry, "modify by return_pyry"/*, overhead*/);
benchmark(modify_by_return_intrinsic, "modify by return_intrinsic"/*, overhead*/);
#else
overhead = benchmark(non_modify_assign, "non_modify_assign ");
benchmark(non_modify_two_operand, "non_modify_two_operand", overhead);

View File

@@ -21,7 +21,7 @@ namespace
void test_reverse_bytes()
{
std::cout << "test_reorder...\n";
std::cout << "test_reverse_bytes...\n";
boost::int64_t i64 = 0x0102030405060708LL;
BOOST_TEST_EQ(be::reverse_bytes(i64), 0x0807060504030201LL);
@@ -59,7 +59,7 @@ namespace
BOOST_TEST_EQ(be::reverse_bytes(ui16), 0x0201);
BOOST_TEST_EQ(be::reverse_bytes(be::reverse_bytes(ui16)), ui16);
std::cout << " test_reorder complete\n";
std::cout << " test_reverse_bytes complete\n";
}
const boost::int64_t ni64 = 0x0102030405060708LL;
@@ -118,7 +118,7 @@ namespace
void test_conditional_reverse_bytes()
{
std::cout << "test_conditional_reorder...\n";
std::cout << "test_conditional_reverse_bytes...\n";
BOOST_TEST_EQ(be::big(ni64), bi64);
BOOST_TEST_EQ(be::big(ni32), bi32);
@@ -147,8 +147,23 @@ namespace
BOOST_TEST_EQ(be::little(be::little(nui64)), nui64);
BOOST_TEST_EQ(be::little(be::little(nui32)), nui32);
BOOST_TEST_EQ(be::little(be::little(nui16)), nui16);
std::cout << " test_conditional_reverse_bytes complete\n";
std::cout << " test_conditional_reorder complete\n";
}
void test_compile_time_convert_bytes()
{
std::cout << "test_compile_time_convert_bytes...\n";
BOOST_TEST_EQ((be::convert_bytes<be::order::big, be::order::little>(bi32)), li32);
std::cout << " test_compile_time_convert_bytes complete\n";
}
void test_runtime_convert_bytes()
{
std::cout << "test_runtime_convert_bytes...\n";
std::cout << " test_runtime_convert_bytes complete\n";
}
} // unnamed namespace
@@ -158,6 +173,7 @@ int cpp_main(int, char * [])
std::cerr << std::hex;
test_reverse_bytes();
test_conditional_reverse_bytes();
test_compile_time_convert_bytes();
return ::boost::report_errors();
}

View File

@@ -122,27 +122,28 @@ namespace
if ( memcmp( v.c, "\10\7\6\5\4\3\2\1", 8) == 0 )
{
cout << "This machine is little-endian.\n";
# ifdef BOOST_BIG_INTEGER_OPERATORS
cout << "yet boost/detail/endian.hpp defines BOOST_BIG_INTEGER_OPERATORS.\n"
"You must fix boost/detail/endian.hpp for boost/endian.hpp to work correctly.\n"
"Please report the fix to the Boost mailing list.\n";
# ifndef BOOST_LITTLE_ENDIAN
cout << "yet boost/detail/endian.hpp does not define BOOST_LITTLE_ENDIAN.\n"
"This must be fixed for the Boost Endian library to work correctly on this system.\n"
"Please report this problem to the Boost mailing list.\n";
exit(1);
# endif
}
else if ( memcmp( v.c, "\1\2\3\4\5\6\7\10", 8) == 0 )
{
cout << "This machine is big-endian.\n";
# ifdef BOOST_LITTLE_INTEGER_OPERATORS
cout << "yet boost/detail/endian.hpp defines BOOST__LITTLE_INTEGER_OPERATORS.\n"
"You must fix boost/detail/endian.hpp for boost/endian.hpp to work correctly.\n"
"Please report the fix to the Boost mailing list.\n";
# ifndef BOOST_BIG_ENDIAN
cout << "yet boost/detail/endian.hpp does not define BOOST_BIG_ENDIAN.\n"
"This must be fixed for the Boost Endian library to work correctly on this system.\n"
"Please report this problem to the Boost mailing list.\n";
exit(1);
# endif
}
else
{
cout << "This machine is neither strict big-endian nor strict little-endian\n"
"You must modify boost/endian.hpp for it to work correctly.\n";
"The Boost Endian library must be revised to work correctly on this system.\n";
"Please report this problem to the Boost mailing list.\n";
exit(1);
}
cout << "That should not matter and is presented for your information only.\n";

View File

@@ -87,7 +87,7 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>"$(TargetDir)\$(TargetName).exe"</Command>
<Command>"$(TargetDir)\$(TargetName).exe" 10000</Command>
<Message>Executing test...</Message>
</PostBuildEvent>
</ItemDefinitionGroup>

View File

@@ -1,6 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C++ Express 2010
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Desktop
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "endian_test", "endian_test\endian_test.vcxproj", "{74C201F3-8308-40BE-BC0F-24974DEAF405}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "endian_in_union_test", "endian_in_union_test\endian_in_union_test.vcxproj", "{3926C6DC-9D1E-4227-BEF5-81F5EC621A75}"
@@ -34,9 +34,7 @@ Global
{A0060A5B-673C-4AD8-BD08-A5C643B1A1CB}.Release|Win32.ActiveCfg = Release|Win32
{A0060A5B-673C-4AD8-BD08-A5C643B1A1CB}.Release|Win32.Build.0 = Release|Win32
{8420E151-B23B-4651-B526-6AB11EF1E278}.Debug|Win32.ActiveCfg = Debug|Win32
{8420E151-B23B-4651-B526-6AB11EF1E278}.Debug|Win32.Build.0 = Debug|Win32
{8420E151-B23B-4651-B526-6AB11EF1E278}.Release|Win32.ActiveCfg = Release|Win32
{8420E151-B23B-4651-B526-6AB11EF1E278}.Release|Win32.Build.0 = Release|Win32
{8638A3D8-D121-40BF-82E5-127F1B1B2CB2}.Debug|Win32.ActiveCfg = Debug|Win32
{8638A3D8-D121-40BF-82E5-127F1B1B2CB2}.Debug|Win32.Build.0 = Debug|Win32
{8638A3D8-D121-40BF-82E5-127F1B1B2CB2}.Release|Win32.ActiveCfg = Release|Win32