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