1 #ifndef ASM_UNALIGNED_H
2 #define ASM_UNALIGNED_H
3 
4 #include <assert.h>
5 #include <linux/types.h>
6 
7 #define _LITTLE_ENDIAN 1
8 
_isLittleEndian(void)9 static unsigned _isLittleEndian(void)
10 {
11     const union { uint32_t u; uint8_t c[4]; } one = { 1 };
12     assert(_LITTLE_ENDIAN == one.c[0]);
13     return _LITTLE_ENDIAN;
14 }
15 
_swap16(uint16_t in)16 static uint16_t _swap16(uint16_t in)
17 {
18     return ((in & 0xF) << 8) + ((in & 0xF0) >> 8);
19 }
20 
_swap32(uint32_t in)21 static uint32_t _swap32(uint32_t in)
22 {
23     return __builtin_bswap32(in);
24 }
25 
_swap64(uint64_t in)26 static uint64_t _swap64(uint64_t in)
27 {
28     return __builtin_bswap64(in);
29 }
30 
31 /* Little endian */
get_unaligned_le16(const void * memPtr)32 static uint16_t get_unaligned_le16(const void* memPtr)
33 {
34     uint16_t val;
35     __builtin_memcpy(&val, memPtr, sizeof(val));
36     if (!_isLittleEndian()) _swap16(val);
37     return val;
38 }
39 
get_unaligned_le32(const void * memPtr)40 static uint32_t get_unaligned_le32(const void* memPtr)
41 {
42     uint32_t val;
43     __builtin_memcpy(&val, memPtr, sizeof(val));
44     if (!_isLittleEndian()) _swap32(val);
45     return val;
46 }
47 
get_unaligned_le64(const void * memPtr)48 static uint64_t get_unaligned_le64(const void* memPtr)
49 {
50     uint64_t val;
51     __builtin_memcpy(&val, memPtr, sizeof(val));
52     if (!_isLittleEndian()) _swap64(val);
53     return val;
54 }
55 
put_unaligned_le16(uint16_t value,void * memPtr)56 static void put_unaligned_le16(uint16_t value, void* memPtr)
57 {
58     if (!_isLittleEndian()) value = _swap16(value);
59     __builtin_memcpy(memPtr, &value, sizeof(value));
60 }
61 
put_unaligned_le32(uint32_t value,void * memPtr)62 static void put_unaligned_le32(uint32_t value, void* memPtr)
63 {
64     if (!_isLittleEndian()) value = _swap32(value);
65     __builtin_memcpy(memPtr, &value, sizeof(value));
66 }
67 
put_unaligned_le64(uint64_t value,void * memPtr)68 static void put_unaligned_le64(uint64_t value, void* memPtr)
69 {
70     if (!_isLittleEndian()) value = _swap64(value);
71     __builtin_memcpy(memPtr, &value, sizeof(value));
72 }
73 
74 /* big endian */
get_unaligned_be32(const void * memPtr)75 static uint32_t get_unaligned_be32(const void* memPtr)
76 {
77     uint32_t val;
78     __builtin_memcpy(&val, memPtr, sizeof(val));
79     if (_isLittleEndian()) _swap32(val);
80     return val;
81 }
82 
get_unaligned_be64(const void * memPtr)83 static uint64_t get_unaligned_be64(const void* memPtr)
84 {
85     uint64_t val;
86     __builtin_memcpy(&val, memPtr, sizeof(val));
87     if (_isLittleEndian()) _swap64(val);
88     return val;
89 }
90 
put_unaligned_be32(uint32_t value,void * memPtr)91 static void put_unaligned_be32(uint32_t value, void* memPtr)
92 {
93     if (_isLittleEndian()) value = _swap32(value);
94     __builtin_memcpy(memPtr, &value, sizeof(value));
95 }
96 
put_unaligned_be64(uint64_t value,void * memPtr)97 static void put_unaligned_be64(uint64_t value, void* memPtr)
98 {
99     if (_isLittleEndian()) value = _swap64(value);
100     __builtin_memcpy(memPtr, &value, sizeof(value));
101 }
102 
103 /* generic */
104 extern void __bad_unaligned_access_size(void);
105 
106 #define __get_unaligned_le(ptr) ((typeof(*(ptr)))({                            \
107     __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),                         \
108     __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)),      \
109     __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)),      \
110     __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)),      \
111     __bad_unaligned_access_size()))));                                         \
112     }))
113 
114 #define __get_unaligned_be(ptr) ((typeof(*(ptr)))({                            \
115     __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr),                         \
116     __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)),      \
117     __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)),      \
118     __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)),      \
119     __bad_unaligned_access_size()))));                                         \
120     }))
121 
122 #define __put_unaligned_le(val, ptr)                                           \
123   ({                                                                           \
124     void *__gu_p = (ptr);                                                      \
125     switch (sizeof(*(ptr))) {                                                  \
126     case 1:                                                                    \
127       *(uint8_t *)__gu_p = (uint8_t)(val);                                     \
128       break;                                                                   \
129     case 2:                                                                    \
130       put_unaligned_le16((uint16_t)(val), __gu_p);                             \
131       break;                                                                   \
132     case 4:                                                                    \
133       put_unaligned_le32((uint32_t)(val), __gu_p);                             \
134       break;                                                                   \
135     case 8:                                                                    \
136       put_unaligned_le64((uint64_t)(val), __gu_p);                             \
137       break;                                                                   \
138     default:                                                                   \
139       __bad_unaligned_access_size();                                           \
140       break;                                                                   \
141     }                                                                          \
142     (void)0;                                                                   \
143   })
144 
145 #define __put_unaligned_be(val, ptr)                                           \
146   ({                                                                           \
147     void *__gu_p = (ptr);                                                      \
148     switch (sizeof(*(ptr))) {                                                  \
149     case 1:                                                                    \
150       *(uint8_t *)__gu_p = (uint8_t)(val);                                     \
151       break;                                                                   \
152     case 2:                                                                    \
153       put_unaligned_be16((uint16_t)(val), __gu_p);                             \
154       break;                                                                   \
155     case 4:                                                                    \
156       put_unaligned_be32((uint32_t)(val), __gu_p);                             \
157       break;                                                                   \
158     case 8:                                                                    \
159       put_unaligned_be64((uint64_t)(val), __gu_p);                             \
160       break;                                                                   \
161     default:                                                                   \
162       __bad_unaligned_access_size();                                           \
163       break;                                                                   \
164     }                                                                          \
165     (void)0;                                                                   \
166   })
167 
168 #if _LITTLE_ENDIAN
169 #  define get_unaligned __get_unaligned_le
170 #  define put_unaligned __put_unaligned_le
171 #else
172 #  define get_unaligned __get_unaligned_be
173 #  define put_unaligned __put_unaligned_be
174 #endif
175 
176 #endif // ASM_UNALIGNED_H
177