|
| 1 | +/* Portable Snippets - https://gitub.com/nemequ/portable-snippets |
| 2 | + * Created by Evan Nemerson <[email protected]> |
| 3 | + * |
| 4 | + * To the extent possible under law, the authors have waived all |
| 5 | + * copyright and related or neighboring rights to Hedley. For |
| 6 | + * details, see the Creative Commons Zero 1.0 Universal license at |
| 7 | + * https://creativecommons.org/publicdomain/zero/1.0/ |
| 8 | + ****************************************************************** |
| 9 | + * Endianness detection and swapping |
| 10 | + * v1 |
| 11 | + * |
| 12 | + * This is probably a bit overkill; you may want to simplify things a |
| 13 | + * bit (i.e., remove unused macros) if you copy this into your |
| 14 | + * project. |
| 15 | + * |
| 16 | + * Support for PDP endian is incomplete. |
| 17 | + * |
| 18 | + * If you improve this, *please* contribute your change back to us. |
| 19 | + */ |
| 20 | + |
| 21 | +/* TODO: don't require this on old versions of MSVC */ |
| 22 | +#include <stdint.h> |
| 23 | + |
| 24 | +#if !defined(PSNIP_ENDIAN_H) |
| 25 | +#define PSNIP_ENDIAN_H |
| 26 | + |
| 27 | +#if !defined(PSNIP_LITTLE_ENDIAN) |
| 28 | +# define PSNIP_LITTLE_ENDIAN 1234 |
| 29 | +#endif |
| 30 | +#if !defined(PSNIP_BIG_ENDIAN) |
| 31 | +# define PSNIP_BIG_ENDIAN 4321 |
| 32 | +#endif |
| 33 | +#if !defined(PSNIP_PDP_ENDIAN) |
| 34 | +# define PSNIP_PDP_ENDIAN 3412 |
| 35 | +#endif |
| 36 | + |
| 37 | +/* Detection |
| 38 | + * |
| 39 | + * Detecting endianness can be a bit tricky. There isn't really a |
| 40 | + * good standard way of determining endianness, and it's actually |
| 41 | + * possible to mix endianness within a single program. This is |
| 42 | + * currently pretty rare, though. |
| 43 | + * |
| 44 | + * We define PSNIP_BYTE_ORDER to PSNIP_LITTLE_ENDIAN, |
| 45 | + * PSNIP_BIG_ENDIAN, or PSNIP_PDP_ENDIAN. Additionally, you can use |
| 46 | + * the PSNIP_RT_BYTE_ORDER to check the runtime byte order, which is a |
| 47 | + * bit more reliable (though it does introduce some runtime overhead. |
| 48 | + * |
| 49 | + * In the event we are unable to determine endianness at compile-time, |
| 50 | + * PSNIP_BYTE_ORDER is left undefined and you will be forced to rely |
| 51 | + * on PSNIP_RT_BYTE_ORDER. */ |
| 52 | + |
| 53 | +#if !defined(PSNIP_BYTE_ORDER) |
| 54 | +/* GCC (and compilers masquerading as GCC) define __BYTE_ORDER__. */ |
| 55 | +# if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) |
| 56 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 57 | +# elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
| 58 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 59 | +# elif defined(__BYTE_ORDER__) && defined(__ORDER_PDP_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__) |
| 60 | +# define PSNIP_BYTE_ORDER PSNIP_PDP_ENDIAN |
| 61 | +/* We know the endianness of some common architectures. Common |
| 62 | + * architectures not listed (ARM, POWER, MIPS, etc.) here are |
| 63 | + * bi-endian. */ |
| 64 | +# elif defined(__amd64) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) |
| 65 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 66 | +# elif defined(__s390x__) || defined(__zarch__) |
| 67 | +# define PSNIP_BYTE_ORDER PSNIP_BIG_ENDIAN |
| 68 | +/* Looks like we'll have to rely on the platform. If we're missing a |
| 69 | + * platform, please let us know. */ |
| 70 | +# elif defined(_WIN32) |
| 71 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 72 | +# elif defined(sun) || defined(__sun) /* Solaris */ |
| 73 | +# include <sys/byteorder.h> |
| 74 | +# if defined(_LITTLE_ENDIAN) |
| 75 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 76 | +# elif defined(_BIG_ENDIAN) |
| 77 | +# define PSNIP_BYTE_ORDER PSNIP_BIG_ENDIAN |
| 78 | +# endif |
| 79 | +# elif defined(__APPLE__) |
| 80 | +# include <libkern/OSByteOrder.h> |
| 81 | +# if defined(__LITTLE_ENDIAN__) |
| 82 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 83 | +# elif defined(__BIG_ENDIAN__) |
| 84 | +# define PSNIP_BYTE_ORDER PSNIP_BIG_ENDIAN |
| 85 | +# endif |
| 86 | +# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(BSD) |
| 87 | +# include <machine/endian.h> |
| 88 | +# if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN) |
| 89 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 90 | +# elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) |
| 91 | +# define PSNIP_BYTE_ORDER PSNIP_BIG_ENDIAN |
| 92 | +# elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __PDP_ENDIAN) |
| 93 | +# define PSNIP_BYTE_ORDER PSNIP_PDP_ENDIAN |
| 94 | +# endif |
| 95 | +# else |
| 96 | +# include <endian.h> |
| 97 | +# if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) |
| 98 | +# define PSNIP_BYTE_ORDER PSNIP_LITTLE_ENDIAN |
| 99 | +# elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) |
| 100 | +# define PSNIP_BYTE_ORDER PSNIP_BIG_ENDIAN |
| 101 | +# elif defined(__BYTE_ORDER) && defined(__PDP_ENDIAN) && (__BYTE_ORDER == __PDP_ENDIAN) |
| 102 | +# define PSNIP_BYTE_ORDER PSNIP_PDP_ENDIAN |
| 103 | +# endif |
| 104 | +# endif |
| 105 | +#endif |
| 106 | + |
| 107 | +/* PDP endian not yet supported. Patches welcome. */ |
| 108 | +#if defined(PSNIP_BYTE_ORDER) && (PSNIP_BYTE_ORDER == PSNIP_PDP_ENDIAN) |
| 109 | +# undef PSNIP_PDP_ENDIAN |
| 110 | +#endif |
| 111 | + |
| 112 | +/* We need fixed-length types for some stuff. */ |
| 113 | +#if defined(_WIN32) |
| 114 | +# define PSNIP_ENDIAN_UINT16_T unsigned short |
| 115 | +# define PSNIP_ENDIAN_UINT32_T unsigned long |
| 116 | +# define PSNIP_ENDIAN_UINT64_T unsigned __int64 |
| 117 | +#else |
| 118 | +# include <stdint.h> |
| 119 | +# define PSNIP_ENDIAN_UINT16_T uint16_t |
| 120 | +# define PSNIP_ENDIAN_UINT32_T uint32_t |
| 121 | +# define PSNIP_ENDIAN_UINT64_T uint64_t |
| 122 | +#endif |
| 123 | + |
| 124 | +/* Runtime detection. */ |
| 125 | +#define PSNIP_RT_BYTE_ORDER_IS_LE \ |
| 126 | + ((((union { unsigned char bytes[4]; PSNIP_ENDIAN_UINT32_T value; }) { { 1, 2, 3, 4 } }).value) == UINT32_C(0x04030201)) |
| 127 | +#define PSNIP_RT_BYTE_ORDER_IS_BE \ |
| 128 | + ((((union { unsigned char bytes[4]; PSNIP_ENDIAN_UINT32_T value; }) { { 1, 2, 3, 4 } }).value) == UINT32_C(0x01020304)) |
| 129 | +#define PSNIP_RT_BYTE_ORDER \ |
| 130 | + (PSNIP_RT_BYTE_ORDER_IS_LE ? PSNIP_LITTLE_ENDIAN : (PSNIP_RT_BYTE_ORDER_IS_BE ? PSNIP_BIG_ENDIAN : PSNIP_PDP_ENDIAN)) |
| 131 | + |
| 132 | +/* Swapping |
| 133 | + * |
| 134 | + * We define PSNIP_BSWAP16, PSNIP_BSWAP32, and PSNIP_BSWAP64 to |
| 135 | + * unconditionally swap byte orders between big-endian and |
| 136 | + * little-endian. |
| 137 | + * |
| 138 | + * We also define macros for reading and writing values with different |
| 139 | + * endiannesses which will be preprocessed to either a swap operation |
| 140 | + * or identity, depending on the system's endianness as determined at |
| 141 | + * compile time or, if none could be determined, we fall back on |
| 142 | + * runtime detection. These are probably the macros you want to use. |
| 143 | + * They are: |
| 144 | + * |
| 145 | + * PSNIP_READ_LE16, PSNIP_READ_LE32, PSNIP_READ_LE64, |
| 146 | + * PSNIP_READ_BE16, PSNIP_READ_BE32, PSNIP_READ_BE64, |
| 147 | + * PSNIP_WRITE_LE16, PSNIP_WRITE_LE32, PSNIP_WRITE_LE64, |
| 148 | + * PSNIP_WRITE_BE16, PSNIP_WRITE_BE32, PSNIP_WRITE_BE64, |
| 149 | + * |
| 150 | + * Finally, there are runtime-only version of those macros which |
| 151 | + * ignore the compile-time endianness and always check at runtime: |
| 152 | + * |
| 153 | + * PSNIP_READ_RT_LE16, PSNIP_READ_RT_LE32, PSNIP_READ_RT_LE64, |
| 154 | + * PSNIP_READ_RT_BE16, PSNIP_READ_RT_BE32, PSNIP_READ_RT_BE64, |
| 155 | + * PSNIP_WRITE_RT_LE16, PSNIP_WRITE_RT_LE32, PSNIP_WRITE_RT_LE64, |
| 156 | + * PSNIP_WRITE_RT_BE16, PSNIP_WRITE_RT_BE32, PSNIP_WRITE_RT_BE64 |
| 157 | + * |
| 158 | + * Interestingly, GCC and clang will detect that these are for |
| 159 | + * swapping endianness and optimize them into an unconditional bswap, |
| 160 | + * at least when endianness is known at compile time. Intel, OTOH, |
| 161 | + * keeps the runtime checks. |
| 162 | + */ |
| 163 | + |
| 164 | +#if defined(__clang__) && defined(__has_builtin) |
| 165 | +# if !defined(PSNIP_BSWAP16) && __has_builtin(__builtin_bswap16) |
| 166 | +# define PSNIP_BSWAP16(v) __builtin_bswap16((int16_t) (v)) |
| 167 | +# endif |
| 168 | +# if !defined(PSNIP_BSWAP32) && __has_builtin(__builtin_bswap32) |
| 169 | +# define PSNIP_BSWAP32(v) __builtin_bswap32((int32_t) (v)) |
| 170 | +# endif |
| 171 | +# if !defined(PSNIP_BSWAP64) && __has_builtin(__builtin_bswap64) |
| 172 | +# define PSNIP_BSWAP64(v) __builtin_bswap64((int64_t) (v)) |
| 173 | +# endif |
| 174 | +#endif |
| 175 | + |
| 176 | +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) |
| 177 | +# if !defined(PSNIP_BSWAP16) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) |
| 178 | +# define PSNIP_BSWAP16(v) __builtin_bswap16((int16_t) (v)) |
| 179 | +# endif |
| 180 | +# if !defined(PSNIP_BSWAP32) |
| 181 | +# define PSNIP_BSWAP32(v) __builtin_bswap32((int32_t) (v)) |
| 182 | +# endif |
| 183 | +# if !defined(PSNIP_BSWAP64) |
| 184 | +# define PSNIP_BSWAP64(v) __builtin_bswap64((int64_t) (v)) |
| 185 | +# endif |
| 186 | +#endif |
| 187 | + |
| 188 | +#if defined(_MSC_VER) && _MSC_VER >= 1310 |
| 189 | +# include <stdlib.h> |
| 190 | +# if !defined(PSNIP_BSWAP16) |
| 191 | +# define PSNIP_BSWAP16(v) _byteswap_ushort((unsigned short) (v)) |
| 192 | +# endif |
| 193 | +# if !defined(PSNIP_BSWAP16) |
| 194 | +# define PSNIP_BSWAP32(v) _byteswap_ulong((unsigned long) (v)) |
| 195 | +# endif |
| 196 | +# if !defined(PSNIP_BSWAP16) |
| 197 | +# define PSNIP_BSWAP64(v) _byteswap_uint64((unsigned __int64) (v)) |
| 198 | +# endif |
| 199 | +#endif |
| 200 | + |
| 201 | +#if !defined(PSNIP_BSWAP16) |
| 202 | +# define PSNIP_BSWAP16(v) \ |
| 203 | + (((((PSNIP_ENDIAN_UINT16_T) (v)) & UINT16_C(0xff00)) >> 8) | \ |
| 204 | + ((((PSNIP_ENDIAN_UINT16_T) (v)) & UINT16_C(0x00ff)) << 8)) |
| 205 | +#endif |
| 206 | +#if !defined(PSNIP_BSWAP32) |
| 207 | +# define PSNIP_BSWAP32(v) \ |
| 208 | + (((((PSNIP_ENDIAN_UINT32_T) (v)) & UINT32_C(0xff000000)) >> 24) | \ |
| 209 | + ((((PSNIP_ENDIAN_UINT32_T) (v)) & UINT32_C(0x00ff0000)) >> 8) | \ |
| 210 | + ((((PSNIP_ENDIAN_UINT32_T) (v)) & UINT32_C(0x0000ff00)) << 8) | \ |
| 211 | + ((((PSNIP_ENDIAN_UINT32_T) (v)) & UINT32_C(0x000000ff)) << 24)) |
| 212 | +#endif |
| 213 | +#if !defined(PSNIP_BSWAP64) |
| 214 | +# define PSNIP_BSWAP64(v) \ |
| 215 | + (((((PSNIP_ENDIAN_UINT64_T) (v)) & (0xff00000000000000ULL)) >> 56) | \ |
| 216 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x00ff000000000000ULL)) >> 40) | \ |
| 217 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x0000ff0000000000ULL)) >> 24) | \ |
| 218 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x000000ff00000000ULL)) >> 8) | \ |
| 219 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x00000000ff000000ULL)) << 8) | \ |
| 220 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x0000000000ff0000ULL)) << 24) | \ |
| 221 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x000000000000ff00ULL)) << 40) | \ |
| 222 | + ((((PSNIP_ENDIAN_UINT64_T) (v)) & (0x00000000000000ffULL)) << 56)); |
| 223 | +#endif |
| 224 | + |
| 225 | +static inline PSNIP_ENDIAN_UINT16_T psnip_io_le16(PSNIP_ENDIAN_UINT16_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_LITTLE_ENDIAN ? (v) : PSNIP_BSWAP16(v)); } |
| 226 | +static inline PSNIP_ENDIAN_UINT32_T psnip_io_le32(PSNIP_ENDIAN_UINT32_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_LITTLE_ENDIAN ? (v) : PSNIP_BSWAP32(v)); } |
| 227 | +static inline PSNIP_ENDIAN_UINT64_T psnip_io_le64(PSNIP_ENDIAN_UINT64_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_LITTLE_ENDIAN ? (v) : PSNIP_BSWAP64(v)); } |
| 228 | +static inline PSNIP_ENDIAN_UINT16_T psnip_io_be16(PSNIP_ENDIAN_UINT16_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_BIG_ENDIAN ? (v) : PSNIP_BSWAP16(v)); } |
| 229 | +static inline PSNIP_ENDIAN_UINT32_T psnip_io_be32(PSNIP_ENDIAN_UINT32_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_BIG_ENDIAN ? (v) : PSNIP_BSWAP32(v)); } |
| 230 | +static inline PSNIP_ENDIAN_UINT64_T psnip_io_be64(PSNIP_ENDIAN_UINT64_T v) { return (PSNIP_RT_BYTE_ORDER == PSNIP_BIG_ENDIAN ? (v) : PSNIP_BSWAP64(v)); } |
| 231 | + |
| 232 | +#define PSNIP_READ_RT_LE16(v) psnip_io_le16(v) |
| 233 | +#define PSNIP_READ_RT_LE32(v) psnip_io_le32(v) |
| 234 | +#define PSNIP_READ_RT_LE64(v) psnip_io_le64(v) |
| 235 | +#define PSNIP_READ_RT_BE16(v) psnip_io_be16(v) |
| 236 | +#define PSNIP_READ_RT_BE32(v) psnip_io_be32(v) |
| 237 | +#define PSNIP_READ_RT_BE64(v) psnip_io_be64(v) |
| 238 | + |
| 239 | +#if defined(PSNIP_BYTE_ORDER) |
| 240 | +# if PSNIP_BYTE_ORDER == PSNIP_LITTLE_ENDIAN |
| 241 | +# define PSNIP_READ_LE16(v) (v) |
| 242 | +# define PSNIP_READ_LE32(v) (v) |
| 243 | +# define PSNIP_READ_LE64(v) (v) |
| 244 | +# define PSNIP_READ_BE16(v) PSNIP_BSWAP16(v) |
| 245 | +# define PSNIP_READ_BE32(v) PSNIP_BSWAP32(v) |
| 246 | +# define PSNIP_READ_BE64(v) PSNIP_BSWAP64(v) |
| 247 | +# elif PSNIP_BYTE_ORDER == PSNIP_BIG_ENDIAN |
| 248 | +# define PSNIP_READ_LE16(v) PSNIP_BSWAP16(v) |
| 249 | +# define PSNIP_READ_LE32(v) PSNIP_BSWAP32(v) |
| 250 | +# define PSNIP_READ_LE64(v) PSNIP_BSWAP64(v) |
| 251 | +# define PSNIP_READ_BE16(v) (v) |
| 252 | +# define PSNIP_READ_BE32(v) (v) |
| 253 | +# define PSNIP_READ_BE64(v) (v) |
| 254 | +# endif |
| 255 | +#else |
| 256 | +# define PSNIP_READ_LE16(v) PSNIP_READ_RT_LE16(v) |
| 257 | +# define PSNIP_READ_LE32(v) PSNIP_READ_RT_LE32(v) |
| 258 | +# define PSNIP_READ_LE64(v) PSNIP_READ_RT_LE64(v) |
| 259 | +# define PSNIP_READ_BE16(v) PSNIP_READ_RT_BE16(v) |
| 260 | +# define PSNIP_READ_BE32(v) PSNIP_READ_RT_BE32(v) |
| 261 | +# define PSNIP_READ_BE64(v) PSNIP_READ_RT_BE64(v) |
| 262 | +#endif |
| 263 | + |
| 264 | +#define PSNIP_WRITE_LE16(v) PSNIP_READ_LE16(v) |
| 265 | +#define PSNIP_WRITE_LE32(v) PSNIP_READ_LE32(v) |
| 266 | +#define PSNIP_WRITE_LE64(v) PSNIP_READ_LE64(v) |
| 267 | +#define PSNIP_WRITE_BE16(v) PSNIP_READ_BE16(v) |
| 268 | +#define PSNIP_WRITE_BE32(v) PSNIP_READ_BE32(v) |
| 269 | +#define PSNIP_WRITE_BE64(v) PSNIP_READ_BE64(v) |
| 270 | +#define PSNIP_WRITE_RT_LE16(v) PSNIP_READ_RT_LE16(v) |
| 271 | +#define PSNIP_WRITE_RT_LE32(v) PSNIP_READ_RT_LE32(v) |
| 272 | +#define PSNIP_WRITE_RT_LE64(v) PSNIP_READ_RT_LE64(v) |
| 273 | +#define PSNIP_WRITE_RT_BE16(v) PSNIP_READ_RT_BE16(v) |
| 274 | +#define PSNIP_WRITE_RT_BE32(v) PSNIP_READ_RT_BE32(v) |
| 275 | +#define PSNIP_WRITE_RT_BE64(v) PSNIP_READ_RT_BE64(v) |
| 276 | + |
| 277 | +#endif /* !defined(PSNIP_ENDIAN_H) */ |
0 commit comments