1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (retro_endianness.h).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#ifndef __LIBRETRO_SDK_ENDIANNESS_H
24#define __LIBRETRO_SDK_ENDIANNESS_H
25
26#include <retro_inline.h>
27#include <stdint.h>
28#include <stdlib.h>
29
30#if defined(_MSC_VER) && _MSC_VER > 1200
31#define SWAP16 _byteswap_ushort
32#define SWAP32 _byteswap_ulong
33#else
34static INLINE uint16_t SWAP16(uint16_t x)
35{
36 return ((x & 0x00ff) << 8) |
37 ((x & 0xff00) >> 8);
38}
39
40static INLINE uint32_t SWAP32(uint32_t x)
41{
42 return ((x & 0x000000ff) << 24) |
43 ((x & 0x0000ff00) << 8) |
44 ((x & 0x00ff0000) >> 8) |
45 ((x & 0xff000000) >> 24);
46}
47
48#endif
49
50#if defined(_MSC_VER) && _MSC_VER <= 1200
51static INLINE uint64_t SWAP64(uint64_t val)
52{
53 return
54 ((val & 0x00000000000000ff) << 56)
55 | ((val & 0x000000000000ff00) << 40)
56 | ((val & 0x0000000000ff0000) << 24)
57 | ((val & 0x00000000ff000000) << 8)
58 | ((val & 0x000000ff00000000) >> 8)
59 | ((val & 0x0000ff0000000000) >> 24)
60 | ((val & 0x00ff000000000000) >> 40)
61 | ((val & 0xff00000000000000) >> 56);
62}
63#else
64static INLINE uint64_t SWAP64(uint64_t val)
65{
66 return ((val & 0x00000000000000ffULL) << 56)
67 | ((val & 0x000000000000ff00ULL) << 40)
68 | ((val & 0x0000000000ff0000ULL) << 24)
69 | ((val & 0x00000000ff000000ULL) << 8)
70 | ((val & 0x000000ff00000000ULL) >> 8)
71 | ((val & 0x0000ff0000000000ULL) >> 24)
72 | ((val & 0x00ff000000000000ULL) >> 40)
73 | ((val & 0xff00000000000000ULL) >> 56);
74}
75#endif
76
77
78#if defined (LSB_FIRST) || defined (MSB_FIRST)
79# warning Defining MSB_FIRST and LSB_FIRST in compile options is deprecated
80# undef LSB_FIRST
81# undef MSB_FIRST
82#endif
83
84#ifdef _MSC_VER
85/* MSVC pre-defines macros depending on target arch */
86#if defined (_M_IX86) || defined (_M_AMD64) || defined (_M_ARM) || defined (_M_ARM64)
87#define LSB_FIRST 1
88#elif _M_PPC
89#define MSB_FIRST 1
90#else
91/* MSVC can run on _M_ALPHA and _M_IA64 too, but they're both bi-endian; need to find what mode MSVC runs them at */
92#error "unknown platform, can't determine endianness"
93#endif
94#else
95#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
96#define MSB_FIRST 1
97#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
98#define LSB_FIRST 1
99#else
100#error "Invalid endianness macros"
101#endif
102#endif
103
104#if defined(MSB_FIRST) && defined(LSB_FIRST)
105# error "Bug in LSB_FIRST/MSB_FIRST definition"
106#endif
107
108#if !defined(MSB_FIRST) && !defined(LSB_FIRST)
109# error "Bug in LSB_FIRST/MSB_FIRST definition"
110#endif
111
112#ifdef MSB_FIRST
113# define RETRO_IS_BIG_ENDIAN 1
114# define RETRO_IS_LITTLE_ENDIAN 0
115/* For compatibility */
116# define WORDS_BIGENDIAN 1
117#else
118# define RETRO_IS_BIG_ENDIAN 0
119# define RETRO_IS_LITTLE_ENDIAN 1
120/* For compatibility */
121# undef WORDS_BIGENDIAN
122#endif
123
124
125/**
126 * is_little_endian:
127 *
128 * Checks if the system is little endian or big-endian.
129 *
130 * Returns: greater than 0 if little-endian,
131 * otherwise big-endian.
132 **/
133#define is_little_endian() RETRO_IS_LITTLE_ENDIAN
134
135/**
136 * swap_if_big64:
137 * @val : unsigned 64-bit value
138 *
139 * Byteswap unsigned 64-bit value if system is big-endian.
140 *
141 * Returns: Byteswapped value in case system is big-endian,
142 * otherwise returns same value.
143 **/
144
145#if RETRO_IS_BIG_ENDIAN
146#define swap_if_big64(val) (SWAP64(val))
147#elif RETRO_IS_LITTLE_ENDIAN
148#define swap_if_big64(val) (val)
149#endif
150
151/**
152 * swap_if_big32:
153 * @val : unsigned 32-bit value
154 *
155 * Byteswap unsigned 32-bit value if system is big-endian.
156 *
157 * Returns: Byteswapped value in case system is big-endian,
158 * otherwise returns same value.
159 **/
160
161#if RETRO_IS_BIG_ENDIAN
162#define swap_if_big32(val) (SWAP32(val))
163#elif RETRO_IS_LITTLE_ENDIAN
164#define swap_if_big32(val) (val)
165#endif
166
167/**
168 * swap_if_little64:
169 * @val : unsigned 64-bit value
170 *
171 * Byteswap unsigned 64-bit value if system is little-endian.
172 *
173 * Returns: Byteswapped value in case system is little-endian,
174 * otherwise returns same value.
175 **/
176
177#if RETRO_IS_BIG_ENDIAN
178#define swap_if_little64(val) (val)
179#elif RETRO_IS_LITTLE_ENDIAN
180#define swap_if_little64(val) (SWAP64(val))
181#endif
182
183/**
184 * swap_if_little32:
185 * @val : unsigned 32-bit value
186 *
187 * Byteswap unsigned 32-bit value if system is little-endian.
188 *
189 * Returns: Byteswapped value in case system is little-endian,
190 * otherwise returns same value.
191 **/
192
193#if RETRO_IS_BIG_ENDIAN
194#define swap_if_little32(val) (val)
195#elif RETRO_IS_LITTLE_ENDIAN
196#define swap_if_little32(val) (SWAP32(val))
197#endif
198
199/**
200 * swap_if_big16:
201 * @val : unsigned 16-bit value
202 *
203 * Byteswap unsigned 16-bit value if system is big-endian.
204 *
205 * Returns: Byteswapped value in case system is big-endian,
206 * otherwise returns same value.
207 **/
208
209#if RETRO_IS_BIG_ENDIAN
210#define swap_if_big16(val) (SWAP16(val))
211#elif RETRO_IS_LITTLE_ENDIAN
212#define swap_if_big16(val) (val)
213#endif
214
215/**
216 * swap_if_little16:
217 * @val : unsigned 16-bit value
218 *
219 * Byteswap unsigned 16-bit value if system is little-endian.
220 *
221 * Returns: Byteswapped value in case system is little-endian,
222 * otherwise returns same value.
223 **/
224
225#if RETRO_IS_BIG_ENDIAN
226#define swap_if_little16(val) (val)
227#elif RETRO_IS_LITTLE_ENDIAN
228#define swap_if_little16(val) (SWAP16(val))
229#endif
230
231/**
232 * store32be:
233 * @addr : pointer to unsigned 32-bit buffer
234 * @data : unsigned 32-bit value to write
235 *
236 * Write data to address. Endian-safe. Byteswaps the data
237 * first if necessary before storing it.
238 **/
239static INLINE void store32be(uint32_t *addr, uint32_t data)
240{
241 *addr = swap_if_little32(data);
242}
243
244/**
245 * load32be:
246 * @addr : pointer to unsigned 32-bit buffer
247 *
248 * Load value from address. Endian-safe.
249 *
250 * Returns: value from address, byte-swapped if necessary.
251 **/
252static INLINE uint32_t load32be(const uint32_t *addr)
253{
254 return swap_if_little32(*addr);
255}
256
257/**
258 * retro_cpu_to_le16:
259 * @val : unsigned 16-bit value
260 *
261 * Convert unsigned 16-bit value from system to little-endian.
262 *
263 * Returns: Little-endian representation of val.
264 **/
265
266#define retro_cpu_to_le16(val) swap_if_big16(val)
267
268/**
269 * retro_cpu_to_le32:
270 * @val : unsigned 32-bit value
271 *
272 * Convert unsigned 32-bit value from system to little-endian.
273 *
274 * Returns: Little-endian representation of val.
275 **/
276
277#define retro_cpu_to_le32(val) swap_if_big32(val)
278
279/**
280 * retro_cpu_to_le64:
281 * @val : unsigned 64-bit value
282 *
283 * Convert unsigned 64-bit value from system to little-endian.
284 *
285 * Returns: Little-endian representation of val.
286 **/
287
288#define retro_cpu_to_le64(val) swap_if_big64(val)
289
290/**
291 * retro_le_to_cpu16:
292 * @val : unsigned 16-bit value
293 *
294 * Convert unsigned 16-bit value from little-endian to native.
295 *
296 * Returns: Native representation of little-endian val.
297 **/
298
299#define retro_le_to_cpu16(val) swap_if_big16(val)
300
301/**
302 * retro_le_to_cpu32:
303 * @val : unsigned 32-bit value
304 *
305 * Convert unsigned 32-bit value from little-endian to native.
306 *
307 * Returns: Native representation of little-endian val.
308 **/
309
310#define retro_le_to_cpu32(val) swap_if_big32(val)
311
312/**
313 * retro_le_to_cpu16:
314 * @val : unsigned 64-bit value
315 *
316 * Convert unsigned 64-bit value from little-endian to native.
317 *
318 * Returns: Native representation of little-endian val.
319 **/
320
321#define retro_le_to_cpu64(val) swap_if_big64(val)
322
323/**
324 * retro_cpu_to_be16:
325 * @val : unsigned 16-bit value
326 *
327 * Convert unsigned 16-bit value from system to big-endian.
328 *
329 * Returns: Big-endian representation of val.
330 **/
331
332#define retro_cpu_to_be16(val) swap_if_little16(val)
333
334/**
335 * retro_cpu_to_be32:
336 * @val : unsigned 32-bit value
337 *
338 * Convert unsigned 32-bit value from system to big-endian.
339 *
340 * Returns: Big-endian representation of val.
341 **/
342
343#define retro_cpu_to_be32(val) swap_if_little32(val)
344
345/**
346 * retro_cpu_to_be64:
347 * @val : unsigned 64-bit value
348 *
349 * Convert unsigned 64-bit value from system to big-endian.
350 *
351 * Returns: Big-endian representation of val.
352 **/
353
354#define retro_cpu_to_be64(val) swap_if_little64(val)
355
356/**
357 * retro_be_to_cpu16:
358 * @val : unsigned 16-bit value
359 *
360 * Convert unsigned 16-bit value from big-endian to native.
361 *
362 * Returns: Native representation of big-endian val.
363 **/
364
365#define retro_be_to_cpu16(val) swap_if_little16(val)
366
367/**
368 * retro_be_to_cpu32:
369 * @val : unsigned 32-bit value
370 *
371 * Convert unsigned 32-bit value from big-endian to native.
372 *
373 * Returns: Native representation of big-endian val.
374 **/
375
376#define retro_be_to_cpu32(val) swap_if_little32(val)
377
378/**
379 * retro_be_to_cpu64:
380 * @val : unsigned 64-bit value
381 *
382 * Convert unsigned 64-bit value from big-endian to native.
383 *
384 * Returns: Native representation of big-endian val.
385 **/
386
387#define retro_be_to_cpu64(val) swap_if_little64(val)
388
389#ifdef __GNUC__
390/* This attribute means that the same memory may be referred through
391 pointers to different size of the object (aliasing). E.g. that u8 *
392 and u32 * may actually be pointing to the same object. */
393#define MAY_ALIAS __attribute__((__may_alias__))
394#else
395#define MAY_ALIAS
396#endif
397
398#pragma pack(push, 1)
399struct retro_unaligned_uint16_s
400{
401 uint16_t val;
402} MAY_ALIAS;
403struct retro_unaligned_uint32_s
404{
405 uint32_t val;
406} MAY_ALIAS;
407struct retro_unaligned_uint64_s
408{
409 uint64_t val;
410} MAY_ALIAS;
411#pragma pack(pop)
412
413typedef struct retro_unaligned_uint16_s retro_unaligned_uint16_t;
414typedef struct retro_unaligned_uint32_s retro_unaligned_uint32_t;
415typedef struct retro_unaligned_uint64_s retro_unaligned_uint64_t;
416
417/* L-value references to unaligned pointers. */
418#define retro_unaligned16(p) (((retro_unaligned_uint16_t *)p)->val)
419#define retro_unaligned32(p) (((retro_unaligned_uint32_t *)p)->val)
420#define retro_unaligned64(p) (((retro_unaligned_uint64_t *)p)->val)
421
422/**
423 * retro_get_unaligned_16be:
424 * @addr : pointer to unsigned 16-bit value
425 *
426 * Convert unsigned unaligned 16-bit value from big-endian to native.
427 *
428 * Returns: Native representation of big-endian val.
429 **/
430
431static INLINE uint16_t retro_get_unaligned_16be(void *addr) {
432 return retro_be_to_cpu16(retro_unaligned16(addr));
433}
434
435/**
436 * retro_get_unaligned_32be:
437 * @addr : pointer to unsigned 32-bit value
438 *
439 * Convert unsigned unaligned 32-bit value from big-endian to native.
440 *
441 * Returns: Native representation of big-endian val.
442 **/
443
444static INLINE uint32_t retro_get_unaligned_32be(void *addr) {
445 return retro_be_to_cpu32(retro_unaligned32(addr));
446}
447
448/**
449 * retro_get_unaligned_64be:
450 * @addr : pointer to unsigned 64-bit value
451 *
452 * Convert unsigned unaligned 64-bit value from big-endian to native.
453 *
454 * Returns: Native representation of big-endian val.
455 **/
456
457static INLINE uint64_t retro_get_unaligned_64be(void *addr) {
458 return retro_be_to_cpu64(retro_unaligned64(addr));
459}
460
461/**
462 * retro_get_unaligned_16le:
463 * @addr : pointer to unsigned 16-bit value
464 *
465 * Convert unsigned unaligned 16-bit value from little-endian to native.
466 *
467 * Returns: Native representation of little-endian val.
468 **/
469
470static INLINE uint16_t retro_get_unaligned_16le(void *addr) {
471 return retro_le_to_cpu16(retro_unaligned16(addr));
472}
473
474/**
475 * retro_get_unaligned_32le:
476 * @addr : pointer to unsigned 32-bit value
477 *
478 * Convert unsigned unaligned 32-bit value from little-endian to native.
479 *
480 * Returns: Native representation of little-endian val.
481 **/
482
483static INLINE uint32_t retro_get_unaligned_32le(void *addr) {
484 return retro_le_to_cpu32(retro_unaligned32(addr));
485}
486
487/**
488 * retro_get_unaligned_64le:
489 * @addr : pointer to unsigned 64-bit value
490 *
491 * Convert unsigned unaligned 64-bit value from little-endian to native.
492 *
493 * Returns: Native representation of little-endian val.
494 **/
495
496static INLINE uint64_t retro_get_unaligned_64le(void *addr) {
497 return retro_le_to_cpu64(retro_unaligned64(addr));
498}
499
500/**
501 * retro_set_unaligned_16le:
502 * @addr : pointer to unsigned 16-bit value
503 * @val : value to store
504 *
505 * Convert native value to unsigned unaligned 16-bit little-endian value
506 *
507 **/
508
509static INLINE void retro_set_unaligned_16le(void *addr, uint16_t v) {
510 retro_unaligned16(addr) = retro_cpu_to_le16(v);
511}
512
513/**
514 * retro_set_unaligned_32le:
515 * @addr : pointer to unsigned 32-bit value
516 * @val : value to store
517 *
518 * Convert native value to unsigned unaligned 32-bit little-endian value
519 *
520 **/
521
522static INLINE void retro_set_unaligned_32le(void *addr, uint32_t v) {
523 retro_unaligned32(addr) = retro_cpu_to_le32(v);
524}
525
526/**
527 * retro_set_unaligned_32le:
528 * @addr : pointer to unsigned 32-bit value
529 * @val : value to store
530 *
531 * Convert native value to unsigned unaligned 32-bit little-endian value
532 *
533 **/
534
535static INLINE void retro_set_unaligned_64le(void *addr, uint64_t v) {
536 retro_unaligned64(addr) = retro_cpu_to_le64(v);
537}
538
539/**
540 * retro_set_unaligned_16be:
541 * @addr : pointer to unsigned 16-bit value
542 * @val : value to store
543 *
544 * Convert native value to unsigned unaligned 16-bit big-endian value
545 *
546 **/
547
548static INLINE void retro_set_unaligned_16be(void *addr, uint16_t v) {
549 retro_unaligned16(addr) = retro_cpu_to_be16(v);
550}
551
552/**
553 * retro_set_unaligned_32be:
554 * @addr : pointer to unsigned 32-bit value
555 * @val : value to store
556 *
557 * Convert native value to unsigned unaligned 32-bit big-endian value
558 *
559 **/
560
561static INLINE void retro_set_unaligned_32be(void *addr, uint32_t v) {
562 retro_unaligned32(addr) = retro_cpu_to_be32(v);
563}
564
565/**
566 * retro_set_unaligned_32be:
567 * @addr : pointer to unsigned 32-bit value
568 * @val : value to store
569 *
570 * Convert native value to unsigned unaligned 32-bit big-endian value
571 *
572 **/
573
574static INLINE void retro_set_unaligned_64be(void *addr, uint64_t v) {
575 retro_unaligned64(addr) = retro_cpu_to_be64(v);
576}
577
578
579#endif
580