blob: ad9801f1c939ae290c118598c7da87d561dec271 [file] [log] [blame]
////
Copyright 2011-2016 Beman Dawes
Distributed under the Boost Software License, Version 1.0.
(http://www.boost.org/LICENSE_1_0.txt)
////
[#conversion]
# Endian Conversion Functions
:idprefix: conversion_
## Introduction
Header `boost/endian/conversion.hpp` provides byte order reversal and conversion
functions that convert objects of the built-in integer types between native,
big, or little endian byte ordering. User defined types are also supported.
## Reference
Functions are implemented `inline` if appropriate. For {cpp}03 compilers,
`noexcept` is elided. Boost scoped enum emulation is used so that the library
still works for compilers that do not support scoped enums.
### Definitions
*Endianness* refers to the ordering of bytes within internal or external
integers and other arithmetic data. Most-significant byte first is called
*big endian* ordering. Least-significant byte first is called
*little endian* ordering. Other orderings are possible and some CPU
architectures support both big and little ordering.
NOTE: The names are derived from
http://en.wikipedia.org/wiki/Jonathan_Swift[Jonathan Swift]'s satirical novel
_http://en.wikipedia.org/wiki/Gulliver's_Travels[Gulliver's Travels]_, where
rival kingdoms opened their soft-boiled eggs at different ends. Wikipedia has an
extensive description of https://en.wikipedia.org/wiki/Endianness[Endianness].
The standard integral types ({cpp}std [basic.fundamental]) except `bool` and
the scoped enumeration types ({cpp}std [dcl.enum]) are collectively called the
*endian types*. In the absence of padding bits, which is true on the platforms
supported by the Boost.Endian library, endian types have the property that all
of their bit patterns are valid values, which means that when an object of an
endian type has its constituent bytes reversed, the result is another valid value.
This allows `endian_reverse` to take and return by value.
Other built-in types, such as `bool`, `float`, or unscoped enumerations, do not
have the same property, which means that reversing their constituent bytes may
produce an invalid value, leading to undefined behavior. These types are therefore
disallowed in `endian_reverse`, but are still allowed in `endian_reverse_inplace`.
Even if an object becomes invalid as a result of reversing its bytes, as long as
its value is never read, there would be no undefined behavior.
### Header `<boost/endian/conversion.hpp>` Synopsis
[subs=+quotes]
```
#define BOOST_ENDIAN_INTRINSIC_MSG \
"`message describing presence or absence of intrinsics`"
namespace boost
{
namespace endian
{
enum class order
{
native = `see below`,
big = `see below`,
little = `see below`,
};
// Byte reversal functions
template <class Endian>
Endian endian_reverse(Endian x) noexcept;
template <class EndianReversible>
EndianReversible big_to_native(EndianReversible x) noexcept;
template <class EndianReversible>
EndianReversible native_to_big(EndianReversible x) noexcept;
template <class EndianReversible>
EndianReversible little_to_native(EndianReversible x) noexcept;
template <class EndianReversible>
EndianReversible native_to_little(EndianReversible x) noexcept;
template <order O1, order O2, class EndianReversible>
EndianReversible conditional_reverse(EndianReversible x) noexcept;
template <class EndianReversible>
EndianReversible conditional_reverse(EndianReversible x,
order order1, order order2) noexcept;
// In-place byte reversal functions
template <class EndianReversible>
void endian_reverse_inplace(EndianReversible& x) noexcept;
template<class EndianReversibleInplace, std::size_t N>
void endian_reverse_inplace(EndianReversibleInplace (&x)[N]) noexcept;
template <class EndianReversibleInplace>
void big_to_native_inplace(EndianReversibleInplace& x) noexcept;
template <class EndianReversibleInplace>
void native_to_big_inplace(EndianReversibleInplace& x) noexcept;
template <class EndianReversibleInplace>
void little_to_native_inplace(EndianReversibleInplace& x) noexcept;
template <class EndianReversibleInplace>
void native_to_little_inplace(EndianReversibleInplace& x) noexcept;
template <order O1, order O2, class EndianReversibleInplace>
void conditional_reverse_inplace(EndianReversibleInplace& x) noexcept;
template <class EndianReversibleInplace>
void conditional_reverse_inplace(EndianReversibleInplace& x,
order order1, order order2) noexcept;
// Generic load and store functions
template<class T, std::size_t N, order Order>
T endian_load( unsigned char const * p ) noexcept;
template<class T, std::size_t N, order Order>
void endian_store( unsigned char * p, T const & v ) noexcept;
// Convenience load functions
boost::int16_t load_little_s16( unsigned char const * p ) noexcept;
boost::uint16_t load_little_u16( unsigned char const * p ) noexcept;
boost::int16_t load_big_s16( unsigned char const * p ) noexcept;
boost::uint16_t load_big_u16( unsigned char const * p ) noexcept;
boost::int32_t load_little_s24( unsigned char const * p ) noexcept;
boost::uint32_t load_little_u24( unsigned char const * p ) noexcept;
boost::int32_t load_big_s24( unsigned char const * p ) noexcept;
boost::uint32_t load_big_u24( unsigned char const * p ) noexcept;
boost::int32_t load_little_s32( unsigned char const * p ) noexcept;
boost::uint32_t load_little_u32( unsigned char const * p ) noexcept;
boost::int32_t load_big_s32( unsigned char const * p ) noexcept;
boost::uint32_t load_big_u32( unsigned char const * p ) noexcept;
boost::int64_t load_little_s40( unsigned char const * p ) noexcept;
boost::uint64_t load_little_u40( unsigned char const * p ) noexcept;
boost::int64_t load_big_s40( unsigned char const * p ) noexcept;
boost::uint64_t load_big_u40( unsigned char const * p ) noexcept;
boost::int64_t load_little_s48( unsigned char const * p ) noexcept;
boost::uint64_t load_little_u48( unsigned char const * p ) noexcept;
boost::int64_t load_big_s48( unsigned char const * p ) noexcept;
boost::uint64_t load_big_u48( unsigned char const * p ) noexcept;
boost::int64_t load_little_s56( unsigned char const * p ) noexcept;
boost::uint64_t load_little_u56( unsigned char const * p ) noexcept;
boost::int64_t load_big_s56( unsigned char const * p ) noexcept;
boost::uint64_t load_big_u56( unsigned char const * p ) noexcept;
boost::int64_t load_little_s64( unsigned char const * p ) noexcept;
boost::uint64_t load_little_u64( unsigned char const * p ) noexcept;
boost::int64_t load_big_s64( unsigned char const * p ) noexcept;
boost::uint64_t load_big_u64( unsigned char const * p ) noexcept;
// Convenience store functions
void store_little_s16( unsigned char * p, boost::int16_t v ) noexcept;
void store_little_u16( unsigned char * p, boost::uint16_t v ) noexcept;
void store_big_s16( unsigned char * p, boost::int16_t v ) noexcept;
void store_big_u16( unsigned char * p, boost::uint16_t v ) noexcept;
void store_little_s24( unsigned char * p, boost::int32_t v ) noexcept;
void store_little_u24( unsigned char * p, boost::uint32_t v ) noexcept;
void store_big_s24( unsigned char * p, boost::int32_t v ) noexcept;
void store_big_u24( unsigned char * p, boost::uint32_t v ) noexcept;
void store_little_s32( unsigned char * p, boost::int32_t v ) noexcept;
void store_little_u32( unsigned char * p, boost::uint32_t v ) noexcept;
void store_big_s32( unsigned char * p, boost::int32_t v ) noexcept;
void store_big_u32( unsigned char * p, boost::uint32_t v ) noexcept;
void store_little_s40( unsigned char * p, boost::int64_t v ) noexcept;
void store_little_u40( unsigned char * p, boost::uint64_t v ) noexcept;
void store_big_s40( unsigned char * p, boost::int64_t v ) noexcept;
void store_big_u40( unsigned char * p, boost::uint64_t v ) noexcept;
void store_little_s48( unsigned char * p, boost::int64_t v ) noexcept;
void store_little_u48( unsigned char * p, boost::uint64_t v ) noexcept;
void store_big_s48( unsigned char * p, boost::int64_t v ) noexcept;
void store_big_u48( unsigned char * p, boost::uint64_t v ) noexcept;
void store_little_s56( unsigned char * p, boost::int64_t v ) noexcept;
void store_little_u56( unsigned char * p, boost::uint64_t v ) noexcept;
void store_big_s56( unsigned char * p, boost::int64_t v ) noexcept;
void store_big_u56( unsigned char * p, boost::uint64_t v ) noexcept;
void store_little_s64( unsigned char * p, boost::int64_t v ) noexcept;
void store_little_u64( unsigned char * p, boost::uint64_t v ) noexcept;
void store_big_s64( unsigned char * p, boost::int64_t v ) noexcept;
void store_big_u64( unsigned char * p, boost::uint64_t v ) noexcept;
} // namespace endian
} // namespace boost
```
The values of `order::little` and `order::big` shall not be equal to one
another.
The value of `order::native` shall be:
* equal to `order::big` if the execution environment is big endian, otherwise
* equal to `order::little` if the execution environment is little endian,
otherwise
* unequal to both `order::little` and `order::big`.
### Requirements
#### Template argument requirements
The template definitions in the `boost/endian/conversion.hpp` header refer to
various named requirements whose details are set out in the tables in this
subsection. In these tables, `T` is an object or reference type to be supplied
by a {cpp} program instantiating a template; `x` is a value of type (possibly
`const`) `T`; `mlx` is a modifiable lvalue of type `T`.
[#conversion_endianreversible]
##### EndianReversible requirements (in addition to `CopyConstructible`)
[%header,cols=3*]
|===
|Expression |Return |Requirements
|`endian_reverse(x)` |`T`
a|`T` is an endian type or a class type.
If `T` is an endian type, returns the value of `x` with the order of bytes
reversed.
If `T` is a class type, the function:
* Is expected to be implemented by the user, as a non-member function in the same
namespace as `T` that can be found by argument dependent lookup (ADL);
* Should return the value of `x` with the order of bytes reversed for all data members
of types or arrays of types that meet the `EndianReversible` requirements.
|===
[#conversion_endianreversibleinplace]
##### EndianReversibleInplace requirements
[%header,cols=2*]
|===
|Expression |Requirements
|`endian_reverse_inplace(mlx)`
a|`T` is an integral type, an enumeration type, `float`, `double`, a class type,
or an array type.
If `T` is not a class type or an array type, reverses the order of bytes in `mlx`.
If `T` is a class type, the function:
* Is expected to be implemented by the user, as a non-member function in the same
namespace as `T` that can be found by argument dependent lookup (ADL);
* Should reverse the order of bytes of all data members of `mlx` that have types or
arrays of types that meet the `EndianReversible` or `EndianReversibleInplace`
requirements.
If `T` is an array type, calls `endian_reverse_inplace` on each element.
|===
NOTE: Because there is a function template for `endian_reverse_inplace` that
calls `endian_reverse` for class types, only `endian_reverse` is required for a
user-defined type to meet the `EndianReversibleInplace` requirements. Although
user-defined types are not required to supply an `endian_reverse_inplace` function,
doing so may improve efficiency.
#### Customization points for user-defined types (UDTs)
This subsection describes requirements on the Endian library's implementation.
The library's function templates requiring
`<<conversion_endianreversible,EndianReversible>>` are required to perform
reversal of endianness if needed by making an unqualified call to
`endian_reverse()`.
The library's function templates requiring
`<<conversion_endianreversibleinplace,EndianReversibleInplace>>` are required to
perform reversal of endianness if needed by making an unqualified call to
`endian_reverse_inplace()`.
See `example/udt_conversion_example.cpp` for an example user-defined type.
### Byte Reversal Functions
```
template <class Endian>
Endian endian_reverse(Endian x) noexcept;
```
[none]
* {blank}
+
Requires:: `Endian` must be a standard integral type that is not `bool`,
or a scoped enumeration type.
Returns:: `x`, with the order of its constituent bytes reversed.
```
template <class EndianReversible>
EndianReversible big_to_native(EndianReversible x) noexcept;
```
[none]
* {blank}
+
Returns:: `conditional_reverse<order::big, order::native>(x)`.
```
template <class EndianReversible>
EndianReversible native_to_big(EndianReversible x) noexcept;
```
[none]
* {blank}
+
Returns:: `conditional_reverse<order::native, order::big>(x)`.
```
template <class EndianReversible>
EndianReversible little_to_native(EndianReversible x) noexcept;
```
[none]
* {blank}
+
Returns:: `conditional_reverse<order::little, order::native>(x)`.
```
template <class EndianReversible>
EndianReversible native_to_little(EndianReversible x) noexcept;
```
[none]
* {blank}
+
Returns:: `conditional_reverse<order::native, order::little>(x)`.
```
template <order O1, order O2, class EndianReversible>
EndianReversible conditional_reverse(EndianReversible x) noexcept;
```
[none]
* {blank}
+
Returns:: `x` if `O1 == O2,` otherwise `endian_reverse(x)`.
Remarks:: Whether `x` or `endian_reverse(x)` is to be returned shall be
determined at compile time.
```
template <class EndianReversible>
EndianReversible conditional_reverse(EndianReversible x,
order order1, order order2) noexcept;
```
[none]
* {blank}
+
Returns::
`order1 == order2? x: endian_reverse(x)`.
### In-place Byte Reversal Functions
```
template <class EndianReversible>
void endian_reverse_inplace(EndianReversible& x) noexcept;
```
[none]
* {blank}
+
Effects:: When `EndianReversible` is a class type,
`x = endian_reverse(x);`. When `EndianReversible` is an integral
type, an enumeration type, `float`, or `double`, reverses the
order of the constituent bytes of `x`. Otherwise, the program is
ill-formed.
```
template<class EndianReversibleInplace, std::size_t N>
void endian_reverse_inplace(EndianReversibleInplace (&x)[N]) noexcept;
```
[none]
* {blank}
+
Effects:: Calls `endian_reverse_inplace(x[i])` for `i` from `0` to `N-1`.
```
template <class EndianReversibleInplace>
void big_to_native_inplace(EndianReversibleInplace& x) noexcept;
```
[none]
* {blank}
+
Effects:: `conditional_reverse_inplace<order::big, order::native>(x)`.
```
template <class EndianReversibleInplace>
void native_to_big_inplace(EndianReversibleInplace& x) noexcept;
```
[none]
* {blank}
+
Effects:: `conditional_reverse_inplace<order::native, order::big>(x)`.
```
template <class EndianReversibleInplace>
void little_to_native_inplace(EndianReversibleInplace& x) noexcept;
```
[none]
* {blank}
+
Effects:: `conditional_reverse_inplace<order::little, order::native>(x)`.
```
template <class EndianReversibleInplace>
void native_to_little_inplace(EndianReversibleInplace& x) noexcept;
```
[none]
* {blank}
+
Effects:: `conditional_reverse_inplace<order::native, order::little>(x)`.
```
template <order O1, order O2, class EndianReversibleInplace>
void conditional_reverse_inplace(EndianReversibleInplace& x) noexcept;
```
[none]
* {blank}
+
Effects:: None if `O1 == O2,` otherwise `endian_reverse_inplace(x)`.
Remarks:: Which effect applies shall be determined at compile time.
```
template <class EndianReversibleInplace>
void conditional_reverse_inplace(EndianReversibleInplace& x,
order order1, order order2) noexcept;
```
[none]
* {blank}
+
Effects::
If `order1 == order2` then `endian_reverse_inplace(x)`.
### Generic Load and Store Functions
```
template<class T, std::size_t N, order Order>
T endian_load( unsigned char const * p ) noexcept;
```
[none]
* {blank}
+
Requires:: `sizeof(T)` must be 1, 2, 4, or 8. `N` must be between 1 and
`sizeof(T)`, inclusive. `T` must be trivially copyable. If `N` is not
equal to `sizeof(T)`, `T` must be integral or `enum`.
Effects:: Reads `N` bytes starting from `p`, in forward or reverse order
depending on whether `Order` matches the native endianness or not,
interprets the resulting bit pattern as a value of type `T`, and returns it.
If `sizeof(T)` is bigger than `N`, zero-extends when `T` is unsigned,
sign-extends otherwise.
```
template<class T, std::size_t N, order Order>
void endian_store( unsigned char * p, T const & v ) noexcept;
```
[none]
* {blank}
+
Requires:: `sizeof(T)` must be 1, 2, 4, or 8. `N` must be between 1 and
`sizeof(T)`, inclusive. `T` must be trivially copyable. If `N` is not
equal to `sizeof(T)`, `T` must be integral or `enum`.
Effects:: Writes to `p` the `N` least significant bytes from the object
representation of `v`, in forward or reverse order depending on whether
`Order` matches the native endianness or not.
### Convenience Load Functions
```
inline boost::intM_t load_little_sN( unsigned char const * p ) noexcept;
```
[none]
* {blank}
+
Reads an N-bit signed little-endian integer from `p`.
+
Returns:: `endian_load<boost::intM_t, N/8, order::little>( p )`.
```
inline boost::uintM_t load_little_uN( unsigned char const * p ) noexcept;
```
[none]
* {blank}
+
Reads an N-bit unsigned little-endian integer from `p`.
+
Returns:: `endian_load<boost::uintM_t, N/8, order::little>( p )`.
```
inline boost::intM_t load_big_sN( unsigned char const * p ) noexcept;
```
[none]
* {blank}
+
Reads an N-bit signed big-endian integer from `p`.
+
Returns:: `endian_load<boost::intM_t, N/8, order::big>( p )`.
```
inline boost::uintM_t load_big_uN( unsigned char const * p ) noexcept;
```
[none]
* {blank}
+
Reads an N-bit unsigned big-endian integer from `p`.
+
Returns::
`endian_load<boost::uintM_t, N/8, order::big>( p )`.
### Convenience Store Functions
```
inline void store_little_sN( unsigned char * p, boost::intM_t v ) noexcept;
```
[none]
* {blank}
+
Writes an N-bit signed little-endian integer to `p`.
+
Effects:: `endian_store<boost::intM_t, N/8, order::little>( p, v )`.
```
inline void store_little_uN( unsigned char * p, boost::uintM_t v ) noexcept;
```
[none]
* {blank}
+
Writes an N-bit unsigned little-endian integer to `p`.
+
Effects:: `endian_store<boost::uintM_t, N/8, order::little>( p, v )`.
```
inline void store_big_sN( unsigned char * p, boost::intM_t v ) noexcept;
```
[none]
* {blank}
+
Writes an N-bit signed big-endian integer to `p`.
+
Effects:: `endian_store<boost::intM_t, N/8, order::big>( p, v )`.
```
inline void store_big_uN( unsigned char * p, boost::uintM_t v ) noexcept;
```
[none]
* {blank}
+
Writes an N-bit unsigned big-endian integer to `p`.
+
Effects::
`endian_store<boost::uintM_t, N/8, order::big>( p, v )`.
## FAQ
See the <<overview_faq,Overview FAQ>> for a library-wide FAQ.
Why are both value returning and modify-in-place functions provided?::
Returning the result by value is the standard C and {cpp} idiom for functions
that compute a value from an argument. Modify-in-place functions allow cleaner
code in many real-world endian use cases and are more efficient for user-defined
types that have members such as string data that do not need to be reversed.
Thus both forms are provided.
Why not use the Linux names (htobe16, htole16, be16toh, le16toh, etc.) ?::
Those names are non-standard and vary even between POSIX-like operating
systems. A {cpp} library TS was going to use those names, but found they were
sometimes implemented as macros. Since macros do not respect scoping and
namespace rules, to use them would be very error prone.
## Acknowledgements
Tomas Puverle was instrumental in identifying and articulating the need to
support endian conversion as separate from endian integer types. Phil Endecott
suggested the form of the value returning signatures. Vicente Botet and other
reviewers suggested supporting user defined types. General reverse template
implementation approach using `std::reverse` suggested by Mathias Gaunard.
Portable implementation approach for 16, 32, and 64-bit integers suggested by
tymofey, with avoidance of undefined behavior as suggested by Giovanni Piero
Deretta, and a further refinement suggested by Pyry Jahkola. Intrinsic builtins
implementation approach for 16, 32, and 64-bit integers suggested by several
reviewers, and by David Stone, who provided his Boost licensed macro
implementation that became the starting point for
`boost/endian/detail/intrinsic.hpp`. Pierre Talbot provided the
`int8_t endian_reverse()` and templated `endian_reverse_inplace()`
implementations.