roo_io
API Documentation for roo_io
Loading...
Searching...
No Matches
endianness.h
Go to the documentation of this file.
1/**
2* @file endianness.h
3* @brief Convert Endianness of shorts, longs, long longs, regardless of architecture/OS
4*
5* Defines (without pulling in platform-specific network include headers):
6* bswap16, bswap32, bswap64, ntoh16, hton16, ntoh32 hton32, ntoh64, hton64
7*
8* Should support linux / macos / solaris / windows.
9* Supports GCC (on any platform, including embedded), MSVC2015, and clang,
10* and should support intel, solaris, and ibm compilers as well.
11*
12* Copyright 2020 github user jtbr, Released under MIT license
13*
14* SPDX-License-Identifier: MIT OR Apache-2.0
15*/
16
17#ifndef ENDIANNESS_H_
18#define ENDIANNESS_H_
19
20#include <stdlib.h>
21#include <stdint.h>
22#ifdef __cplusplus
23#include <cstring> // for memcpy
24#endif
25
26/* Detect platform endianness at compile time */
27
28// If boost were available on all platforms, could use this instead to detect endianness
29// #include <boost/predef/endian.h>
30
31// When available, these headers can improve platform endianness detection
32#ifdef __has_include // C++17, supported as extension to C++11 in clang, GCC 5+, vs2015
33# if __has_include(<endian.h>)
34# include <endian.h> // gnu libc normally provides, linux
35# elif __has_include(<machine/endian.h>)
36# include <machine/endian.h> //open bsd, macos
37# elif __has_include(<sys/param.h>)
38# include <sys/param.h> // mingw, some bsd (not open/macos)
39# elif __has_include(<sys/isadefs.h>)
40# include <sys/isadefs.h> // solaris
41# endif
42#endif
43
44#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
45# if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
46 (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \
47 (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || \
48 (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || \
49 (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || \
50 defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
51 defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) || \
52 defined(_M_PPC)
53# define __BIG_ENDIAN__
54# elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */\
55 (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ || \
56 (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) || \
57 (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ || \
58 (defined(__sun) && defined(__SVR4) && defined(_LITTLE_ENDIAN)) || /* solaris */ \
59 defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
60 defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
61 defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) || /* msvc for intel processors */ \
62 defined(_M_ARM) /* msvc code on arm executes in little endian mode */
63# define __LITTLE_ENDIAN__
64# endif
65#endif
66
67#if !defined(__LITTLE_ENDIAN__) & !defined(__BIG_ENDIAN__)
68# error "UNKNOWN Platform / endianness. Configure endianness checks for this platform or set explicitly."
69#endif
70
71// #if defined(bswap16) || defined(bswap32) || defined(bswap64) || defined(bswapf) || defined(bswapd)
72// # error "unexpected define!" // freebsd may define these; probably just need to undefine them
73// #endif
74
75/* Define byte-swap functions, using fast processor-native built-ins where possible */
76#if defined(_MSC_VER) // needs to be first because msvc doesn't short-circuit after failing defined(__has_builtin)
77# define bswap16(x) _byteswap_ushort((x))
78# define bswap32(x) _byteswap_ulong((x))
79# define bswap64(x) _byteswap_uint64((x))
80#elif (defined(ESP_PLATFORM) || defined(IDF_VER)) && !defined(__linux__) // ESP-IDF's endian.h already defines these.
81#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
82# define bswap16(x) __builtin_bswap16((x))
83# define bswap32(x) __builtin_bswap32((x))
84# define bswap64(x) __builtin_bswap64((x))
85#elif defined(__has_builtin) && __has_builtin(__builtin_bswap64) /* for clang; gcc 5 fails on this and && shortcircuit fails; must be after GCC check */
86# define bswap16(x) __builtin_bswap16((x))
87# define bswap32(x) __builtin_bswap32((x))
88# define bswap64(x) __builtin_bswap64((x))
89#else
90 /* even in this case, compilers often optimize by using native instructions */
91
92 static inline uint16_t bswap16(uint16_t x) {
93 return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 ));
94 }
95 static inline uint32_t bswap32(uint32_t x) {
96 return ((( x & 0xff000000u ) >> 24 ) |
97 (( x & 0x00ff0000u ) >> 8 ) |
98 (( x & 0x0000ff00u ) << 8 ) |
99 (( x & 0x000000ffu ) << 24 ));
100 }
101 static inline uint64_t bswap64(uint64_t x) {
102 return ((( x & 0xff00000000000000ull ) >> 56 ) |
103 (( x & 0x00ff000000000000ull ) >> 40 ) |
104 (( x & 0x0000ff0000000000ull ) >> 24 ) |
105 (( x & 0x000000ff00000000ull ) >> 8 ) |
106 (( x & 0x00000000ff000000ull ) << 8 ) |
107 (( x & 0x0000000000ff0000ull ) << 24 ) |
108 (( x & 0x000000000000ff00ull ) << 40 ) |
109 (( x & 0x00000000000000ffull ) << 56 ));
110 }
111#endif
112
113//! Byte-swap 32-bit float
114static inline float bswapf(float f) {
115#ifdef __cplusplus
116 static_assert(sizeof(float) == sizeof(uint32_t), "Unexpected float format");
117 /* Problem: de-referencing float pointer as uint32_t breaks strict-aliasing rules for C++ and C, even if it normally works
118 * uint32_t val = bswap32(*(reinterpret_cast<const uint32_t *>(&f)));
119 * return *(reinterpret_cast<float *>(&val));
120 */
121 // memcpy approach is guaranteed to work in C & C++ and fn calls should be optimized out:
122 uint32_t asInt;
123 std::memcpy(&asInt, reinterpret_cast<const void *>(&f), sizeof(uint32_t));
124 asInt = bswap32(asInt);
125 std::memcpy(&f, reinterpret_cast<void *>(&asInt), sizeof(float));
126 return f;
127#else
128 _Static_assert(sizeof(float) == sizeof(uint32_t), "Unexpected float format");
129 // union approach is guaranteed to work in C99 and later (but not in C++, though in practice it normally will):
130 union { uint32_t asInt; float asFloat; } conversion_union;
131 conversion_union.asFloat = f;
132 conversion_union.asInt = bswap32(conversion_union.asInt);
133 return conversion_union.asFloat;
134#endif
135}
136
137//! Byte-swap 64-bit double
138static inline double bswapd(double d) {
139#ifdef __cplusplus
140 static_assert(sizeof(double) == sizeof(uint64_t), "Unexpected double format");
141 uint64_t asInt;
142 std::memcpy(&asInt, reinterpret_cast<const void *>(&d), sizeof(uint64_t));
143 asInt = bswap64(asInt);
144 std::memcpy(&d, reinterpret_cast<void *>(&asInt), sizeof(double));
145 return d;
146#else
147 _Static_assert(sizeof(double) == sizeof(uint64_t), "Unexpected double format");
148 union { uint64_t asInt; double asDouble; } conversion_union;
149 conversion_union.asDouble = d;
150 conversion_union.asInt = bswap64(conversion_union.asInt);
151 return conversion_union.asDouble;
152#endif
153}
154
155
156/* Define network - host byte swaps as needed depending upon platform endianness */
157// (note that network order is big endian)
158
159#if defined(__LITTLE_ENDIAN__)
160# define ntoh16(x) bswap16((x))
161# define hton16(x) bswap16((x))
162# define ntoh32(x) bswap32((x))
163# define hton32(x) bswap32((x))
164# define ntoh64(x) bswap64((x))
165# define hton64(x) bswap64((x))
166# define ntohf(x) bswapf((x))
167# define htonf(x) bswapf((x))
168# define ntohd(x) bswapd((x))
169# define htond(x) bswapd((x))
170#elif defined(__BIG_ENDIAN__)
171# define ntoh16(x) (x)
172# define hton16(x) (x)
173# define ntoh32(x) (x)
174# define hton32(x) (x)
175# define ntoh64(x) (x)
176# define hton64(x) (x)
177# define ntohf(x) (x)
178# define htonf(x) (x)
179# define ntohd(x) (x)
180# define htond(x) (x)
181# else
182# warning "UNKNOWN Platform / endianness; network / host byte swaps not defined."
183#endif
184
185#endif //ENDIANNESS_H_
static uint32_t bswap32(uint32_t x)
Definition endianness.h:95
static uint64_t bswap64(uint64_t x)
Definition endianness.h:101
static float bswapf(float f)
Byte-swap 32-bit float.
Definition endianness.h:114
static uint16_t bswap16(uint16_t x)
Definition endianness.h:92
static double bswapd(double d)
Byte-swap 64-bit double.
Definition endianness.h:138