openMSX
endian.hh
Go to the documentation of this file.
1 #ifndef ENDIAN_HH
2 #define ENDIAN_HH
3 
4 #include "build-info.hh"
5 #include <cstdint>
6 #include <cstring>
7 
8 namespace Endian {
9 
10 // Revese bytes in a 16-bit number: 0x1234 becomes 0x3412
11 static inline uint16_t bswap16(uint16_t x)
12 {
13  // This sequence generates 'optimal' code on a wide range of gcc/clang
14  // versions (a single rotate instruction on x86). The newer compiler
15  // versions also do 'the right thing' for the simpler expression below.
16  // Those newer compilers also support __builtin_bswap16() but that
17  // doesn't generate better code (and is less portable).
18  return ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8);
19  //return (x << 8) | (x >> 8);
20 }
21 
22 // Revese bytes in a 32-bit number: 0x12345678 becomes 0x78563412
23 static inline uint32_t bswap32(uint32_t x)
24 {
25 #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
26  // Starting from gcc-4.3 there's a builtin function for this.
27  // E.g. on x86 this is translated to a single 'bswap' instruction.
28  return __builtin_bswap32(x);
29 #else
30  return (x << 24) |
31  ((x << 8) & 0x00ff0000) |
32  ((x >> 8) & 0x0000ff00) |
33  (x >> 24);
34 #endif
35 }
36 
37 // Revese bytes in a 64-bit value: 0x1122334455667788 becomes 0x8877665544332211
38 static inline uint64_t bswap64(uint64_t x)
39 {
40 #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
41  // Starting from gcc-4.3 there's a builtin function for this.
42  // E.g. on x86 this is translated to a single 'bswap' instruction.
43  return __builtin_bswap64(x);
44 #else
45  return (uint64_t(bswap32(x >> 0)) << 32) |
46  (uint64_t(bswap32(x >> 32)) << 0);
47 #endif
48 }
49 
50 // Use overloading to get a (statically) polymorphic bswap() function.
51 static inline uint16_t bswap(uint16_t x) { return bswap16(x); }
52 static inline uint32_t bswap(uint32_t x) { return bswap32(x); }
53 static inline uint64_t bswap(uint64_t x) { return bswap64(x); }
54 
55 
56 // Identity operator, simply returns the given value.
57 struct Ident {
58  template <typename T> inline T operator()(T t) const { return t; }
59 };
60 
61 // Byte-swap operator, swap bytes in the given value (16 or 32 bit).
62 struct BSwap {
63  template <typename T> inline T operator()(T t) const { return bswap(t); }
64 };
65 
66 // Helper class that stores a value and allows to read/write that value. Though
67 // right before it is loaded/stored the value is transformed by a configurable
68 // operation.
69 // TODO If needed this can be extended with stuff like operator+= ....
70 template<typename T, typename Op> class EndianT {
71 public:
72  EndianT() = default; // leave uninitialized
73  EndianT(T t_) { Op op; t = op(t_); }
74  inline operator T() const { Op op; return op(t); }
75  inline EndianT& operator=(T a) { Op op; t = op(a); return *this; }
76 private:
77  T t;
78 };
79 
80 // Define the types B16, B32, L16, L32.
81 //
82 // Typically these types are used to define the layout of external structures
83 // For example:
84 //
85 // struct FATDirectoryEntry {
86 // char filename[8];
87 // char extension[3];
88 // ...
89 // Endian::L32 size; // 32-bit little endian value
90 // };
91 // ...
92 // unsigned s = myDirEntry.size; // Possibly performs endianess conversion.
93 // yourDirEntry.size = s; // If native endianess is already correct
94 // // this has no extra overhead.
95 //
96 // You can assign and read values in native endianess to values of these types.
97 // So basically in a single location define the structure with the correct
98 // endianess and in all other places use the value as-if it were a native type.
99 //
100 // Note that these types should still be correctly aligned (e.g. L32 should be
101 // 4-byte aligned). For unaligned access use the functions below.
102 //
103 template<bool> struct ConvBig;
104 template<> struct ConvBig <true > : Ident {};
105 template<> struct ConvBig <false> : BSwap {};
106 template<bool> struct ConvLittle;
107 template<> struct ConvLittle<true > : BSwap {};
108 template<> struct ConvLittle<false> : Ident {};
113 static_assert(sizeof(B16) == 2, "must have size 2");
114 static_assert(sizeof(L16) == 2, "must have size 2");
115 static_assert(sizeof(B32) == 4, "must have size 4");
116 static_assert(sizeof(L32) == 4, "must have size 4");
117 static_assert(alignof(B16) <= 2, "may have alignment 2");
118 static_assert(alignof(L16) <= 2, "may have alignment 2");
119 static_assert(alignof(B32) <= 4, "may have alignment 4");
120 static_assert(alignof(L32) <= 4, "may have alignment 4");
121 
122 
123 // Helper functions to read/write aligned 16/32 bit values.
124 static inline void writeB16(void* p, uint16_t x)
125 {
126  *reinterpret_cast<B16*>(p) = x;
127 }
128 static inline void writeL16(void* p, uint16_t x)
129 {
130  *reinterpret_cast<L16*>(p) = x;
131 }
132 static inline void writeB32(void* p, uint32_t x)
133 {
134  *reinterpret_cast<B32*>(p) = x;
135 }
136 static inline void writeL32(void* p, uint32_t x)
137 {
138  *reinterpret_cast<L32*>(p) = x;
139 }
140 
141 static inline uint16_t readB16(const void* p)
142 {
143  return *reinterpret_cast<const B16*>(p);
144 }
145 static inline uint16_t readL16(const void* p)
146 {
147  return *reinterpret_cast<const L16*>(p);
148 }
149 static inline uint32_t readB32(const void* p)
150 {
151  return *reinterpret_cast<const B32*>(p);
152 }
153 static inline uint32_t readL32(const void* p)
154 {
155  return *reinterpret_cast<const L32*>(p);
156 }
157 
158 // Read/write big/little 16/32/64-bit values to/from a (possibly) unaligned
159 // memory location. If the host architecture supports unaligned load/stores
160 // (e.g. x86), these functions perform a single load/store (with possibly an
161 // adjust operation on the value if the endianess is different from the host
162 // endianess). If the architecture does not support unaligned memory operations
163 // (e.g. early ARM architectures), the operation is split into byte accesses.
164 
165 template<bool SWAP, typename T> static inline void write_UA(void* p, T x)
166 {
167  if (SWAP) x = bswap(x);
168  memcpy(p, &x, sizeof(x));
169 }
170 static inline void write_UA_B16(void* p, uint16_t x)
171 {
172  write_UA<!openmsx::OPENMSX_BIGENDIAN>(p, x);
173 }
174 static inline void write_UA_L16(void* p, uint16_t x)
175 {
176  write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x);
177 }
178 static inline void write_UA_B32(void* p, uint32_t x)
179 {
180  write_UA<!openmsx::OPENMSX_BIGENDIAN>(p, x);
181 }
182 static inline void write_UA_L32(void* p, uint32_t x)
183 {
184  write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x);
185 }
186 static inline void write_UA_B64(void* p, uint64_t x)
187 {
188  write_UA<!openmsx::OPENMSX_BIGENDIAN>(p, x);
189 }
190 static inline void write_UA_L64(void* p, uint64_t x)
191 {
192  write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x);
193 }
194 
195 template<bool SWAP, typename T> static inline T read_UA(const void* p)
196 {
197  T x;
198  memcpy(&x, p, sizeof(x));
199  if (SWAP) x = bswap(x);
200  return x;
201 }
202 static inline uint16_t read_UA_B16(const void* p)
203 {
204  return read_UA<!openmsx::OPENMSX_BIGENDIAN, uint16_t>(p);
205 }
206 static inline uint16_t read_UA_L16(const void* p)
207 {
208  return read_UA< openmsx::OPENMSX_BIGENDIAN, uint16_t>(p);
209 }
210 static inline uint32_t read_UA_B32(const void* p)
211 {
212  return read_UA<!openmsx::OPENMSX_BIGENDIAN, uint32_t>(p);
213 }
214 static inline uint32_t read_UA_L32(const void* p)
215 {
216  return read_UA< openmsx::OPENMSX_BIGENDIAN, uint32_t>(p);
217 }
218 static inline uint64_t read_UA_B64(const void* p)
219 {
220  return read_UA<!openmsx::OPENMSX_BIGENDIAN, uint64_t>(p);
221 }
222 static inline uint64_t read_UA_L64(const void* p)
223 {
224  return read_UA< openmsx::OPENMSX_BIGENDIAN, uint64_t>(p);
225 }
226 
227 
228 // Like the types above, but these don't need to be aligned.
229 
230 class UA_B16 {
231 public:
232  inline operator uint16_t() const { return read_UA_B16(x); }
233  inline UA_B16& operator=(uint16_t a) { write_UA_B16(x, a); return *this; }
234 private:
235  uint8_t x[2];
236 };
237 
238 class UA_L16 {
239 public:
240  inline operator uint16_t() const { return read_UA_L16(x); }
241  inline UA_L16& operator=(uint16_t a) { write_UA_L16(x, a); return *this; }
242 private:
243  uint8_t x[2];
244 };
245 
246 class UA_B32 {
247 public:
248  inline operator uint32_t() const { return read_UA_B32(x); }
249  inline UA_B32& operator=(uint32_t a) { write_UA_B32(x, a); return *this; }
250 private:
251  uint8_t x[4];
252 };
253 
254 class UA_L32 {
255 public:
256  inline operator uint32_t() const { return read_UA_L32(x); }
257  inline UA_L32& operator=(uint32_t a) { write_UA_L32(x, a); return *this; }
258 private:
259  uint8_t x[4];
260 };
261 
262 static_assert(sizeof(UA_B16) == 2, "must have size 2");
263 static_assert(sizeof(UA_L16) == 2, "must have size 2");
264 static_assert(sizeof(UA_B32) == 4, "must have size 4");
265 static_assert(sizeof(UA_L32) == 4, "must have size 4");
266 static_assert(alignof(UA_B16) == 1, "must have alignment 1");
267 static_assert(alignof(UA_L16) == 1, "must have alignment 1");
268 static_assert(alignof(UA_B32) == 1, "must have alignment 1");
269 static_assert(alignof(UA_L32) == 1, "must have alignment 1");
270 
271 // Template meta-programming.
272 // Get a type of the same size of the given type that stores the value in a
273 // specific endianess. Typically used in template functions that can work on
274 // either 16 or 32 bit values.
275 // usage:
276 // using LE_T = typename Endian::Little<T>::type;
277 // The type LE_T is now a type that stores values of the same size as 'T'
278 // in little endian format (independent of host endianess).
279 template<typename> struct Little;
280 template<> struct Little<uint8_t > { using type = uint8_t; };
281 template<> struct Little<uint16_t> { using type = L16; };
282 template<> struct Little<uint32_t> { using type = L32; };
283 template<typename> struct Big;
284 template<> struct Big<uint8_t > { using type = uint8_t; };
285 template<> struct Big<uint16_t> { using type = B16; };
286 template<> struct Big<uint32_t> { using type = B32; };
287 
288 } // namespace Endian
289 
290 #endif
T operator()(T t) const
Definition: endian.hh:63
UA_B16 & operator=(uint16_t a)
Definition: endian.hh:233
UA_L32 & operator=(uint32_t a)
Definition: endian.hh:257
Definition: endian.hh:8
EndianT< uint16_t, ConvLittle< openmsx::OPENMSX_BIGENDIAN > > L16
Definition: endian.hh:110
UA_L16 & operator=(uint16_t a)
Definition: endian.hh:241
EndianT< uint32_t, ConvLittle< openmsx::OPENMSX_BIGENDIAN > > L32
Definition: endian.hh:112
UA_B32 & operator=(uint32_t a)
Definition: endian.hh:249
EndianT & operator=(T a)
Definition: endian.hh:75
T operator()(T t) const
Definition: endian.hh:58
EndianT(T t_)
Definition: endian.hh:73
EndianT< uint16_t, ConvBig< openmsx::OPENMSX_BIGENDIAN > > B16
Definition: endian.hh:109
TclObject t
EndianT< uint32_t, ConvBig< openmsx::OPENMSX_BIGENDIAN > > B32
Definition: endian.hh:111