-
Notifications
You must be signed in to change notification settings - Fork 4
/
endian.h
159 lines (145 loc) · 4.65 KB
/
endian.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#pragma once
#include "intwrap.h"
//This one defines:
//Macros END_LITTLE, END_BIG and ENDIAN; ENDIAN is equal to one of the other two. The test is borrowed from byuu's nall.
//end_swap() - Byteswaps an integer.
//end_nat_to_le(), end_le_to_nat(), end_nat_to_be(), end_be_to_nat() - Byteswaps an integer or returns it unmodified, depending on the host endianness.
//Class litend<> and bigend<> - Acts like the given integer type, but is stored by the named endianness internally. Safe to memcpy() and fwrite().
#define END_LITTLE 0x04030201
#define END_BIG 0x01020304
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || \
defined(__i386__) || defined(__amd64__) || \
defined(_M_IX86) || defined(_M_AMD64) || \
defined(__ARM_EABI__) || defined(__arm__)
#define ENDIAN END_LITTLE
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \
defined(__powerpc__) || defined(_M_PPC)
#define ENDIAN END_BIG
#endif
//for crazy endians, just leave it undefined
#if defined(__GNUC__)
//This one is mostly useless (GCC detects the pattern and optimizes it).
//However, MSVC doesn't, so I need the intrinsics. Might as well use both sets.
static uint8_t end_swap(uint8_t n) { return n; }
static uint16_t end_swap(uint16_t n) { return __builtin_bswap16(n); }
static uint32_t end_swap(uint32_t n) { return __builtin_bswap32(n); }
static uint64_t end_swap(uint64_t n) { return __builtin_bswap64(n); }
#elif defined(_MSC_VER)
static uint8_t end_swap(uint8_t n) { return n; }
static uint16_t end_swap(uint16_t n) { return _byteswap_ushort(n); }
static uint32_t end_swap(uint32_t n) { return _byteswap_ulong(n); }
static uint64_t end_swap(uint64_t n) { return _byteswap_uint64(n); }
#else
static uint8_t end_swap(uint8_t n) { return n; }
static uint16_t end_swap(uint16_t n) { return n>>8 | n<<8; }
static uint32_t end_swap(uint32_t n)
{
n = n>>16 | n<<16;
n = (n&0x00FF00FF)<<8 | (n&0xFF00FF00)>>8;
return n;
}
static uint64_t end_swap(uint64_t n)
{
n = n>>32 | n<<32;
n = (n&0x0000FFFF0000FFFF)<<16 | (n&0xFFFF0000FFFF0000)>>16;
n = (n&0x00FF00FF00FF00FF)<<8 | (n&0xFF00FF00FF00FF00)>>8 ;
return n;
}
#endif
static int8_t end_swap(int8_t n) { return (int8_t )end_swap((uint8_t )n); }
static int16_t end_swap(int16_t n) { return (int16_t)end_swap((uint16_t)n); }
static int32_t end_swap(int32_t n) { return (int32_t)end_swap((uint32_t)n); }
static int64_t end_swap(int64_t n) { return (int64_t)end_swap((uint64_t)n); }
#ifdef ENDIAN
#if ENDIAN == END_LITTLE
template<typename T> static T end_nat_to_le(T val) { return val; }
template<typename T> static T end_nat_to_be(T val) { return end_swap(val); }
template<typename T> static T end_le_to_nat(T val) { return val; }
template<typename T> static T end_be_to_nat(T val) { return end_swap(val); }
#elif ENDIAN == END_BIG
template<typename T> static T end_nat_to_le(T val) { return end_swap(val); }
template<typename T> static T end_nat_to_be(T val) { return val; }
template<typename T> static T end_le_to_nat(T val) { return end_swap(val); }
template<typename T> static T end_be_to_nat(T val) { return val; }
#endif
template<typename T, bool little> class endian_core
{
T val;
public:
operator T()
{
if (little == (ENDIAN==END_LITTLE)) return val;
else return end_swap(val);
}
void operator=(T newval)
{
if (little == (ENDIAN==END_LITTLE)) val = newval;
else val = end_swap(newval);
}
};
#else
//This one doesn't optimize properly. While it does get unrolled, it remains as four byte loads, and some shift/or.
template<typename T, bool little> class endian_core
{
union {
T align;
uint8_t bytes[sizeof(T)];
};
public:
operator T()
{
if (little)
{
T ret=0;
for (size_t i=0;i<sizeof(T);i++)
{
ret = (ret<<8) | bytes[i];
}
return ret;
}
else
{
T ret=0;
for (size_t i=0;i<sizeof(T);i++)
{
ret = (ret<<8) | bytes[sizeof(T)-1-i];
}
return ret;
}
}
void operator=(T newval)
{
if ((little && ENDIAN==END_LITTLE) || (!little && ENDIAN==END_BIG))
{
val = newval;
return;
}
if (!little)
{
for (size_t i=0;i<sizeof(T);i++)
{
bytes[sizeof(T)-1-i]=(newval&0xFF);
newval>>=8;
}
}
else
{
for (size_t i=0;i<sizeof(T);i++)
{
bytes[i]=(newval&0xFF);
newval>>=8;
}
}
}
};
#endif
template<typename T> class bigend : public intwrap<endian_core<T, false>, T> {
public:
bigend() {}
bigend(T i) : intwrap<endian_core<T, false>, T>(i) {} // why does C++ need so much irritating cruft
};
template<typename T> class litend : public intwrap<endian_core<T, true>, T> {
public:
litend() {}
litend(T i) : intwrap<endian_core<T, true>, T>(i) {}
};