Commit | Line | Data |
---|---|---|
d79865b9 MD |
1 | #ifndef _BABELTRACE_BITFIELD_H |
2 | #define _BABELTRACE_BITFIELD_H | |
6dc2ca62 MD |
3 | |
4 | /* | |
d79865b9 | 5 | * BabelTrace |
6dc2ca62 MD |
6 | * |
7 | * Bitfields read/write functions. | |
8 | * | |
9 | * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
10 | * | |
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
12 | * of this software and associated documentation files (the "Software"), to deal | |
13 | * in the Software without restriction, including without limitation the rights | |
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
15 | * copies of the Software, and to permit persons to whom the Software is | |
16 | * furnished to do so, subject to the following conditions: | |
17 | * | |
18 | * The above copyright notice and this permission notice shall be included in | |
19 | * all copies or substantial portions of the Software. | |
20 | */ | |
21 | ||
22 | #include <stdint.h> /* C99 5.2.4.2 Numerical limits */ | |
23 | #include <limits.h> /* C99 5.2.4.2 Numerical limits */ | |
24 | #include <endian.h> /* Non-standard BIG_ENDIAN, LITTLE_ENDIAN, BYTE_ORDER */ | |
25 | #include <assert.h> | |
26 | ||
08228826 | 27 | /* We can't shift a int from 32 bit, >> 32 and << 32 on int is undefined */ |
d79865b9 | 28 | #define _bt_piecewise_rshift(v, shift) \ |
6dc2ca62 MD |
29 | ({ \ |
30 | unsigned long sb = (shift) / (sizeof(v) * CHAR_BIT - 1); \ | |
31 | unsigned long final = (shift) % (sizeof(v) * CHAR_BIT - 1); \ | |
32 | typeof(v) _v = (v); \ | |
33 | \ | |
34 | for (; sb; sb--) \ | |
35 | _v >>= sizeof(v) * CHAR_BIT - 1; \ | |
36 | _v >>= final; \ | |
37 | }) | |
38 | ||
d79865b9 | 39 | #define _bt_piecewise_lshift(v, shift) \ |
08228826 MD |
40 | ({ \ |
41 | unsigned long sb = (shift) / (sizeof(v) * CHAR_BIT - 1); \ | |
42 | unsigned long final = (shift) % (sizeof(v) * CHAR_BIT - 1); \ | |
43 | typeof(v) _v = (v); \ | |
44 | \ | |
45 | for (; sb; sb--) \ | |
46 | _v <<= sizeof(v) * CHAR_BIT - 1; \ | |
47 | _v <<= final; \ | |
48 | }) | |
49 | ||
d79865b9 | 50 | #define _bt_is_signed_type(type) (((type)(-1)) < 0) |
08228826 | 51 | |
d79865b9 | 52 | #define _bt_unsigned_cast(type, v) \ |
08228826 MD |
53 | ({ \ |
54 | (sizeof(v) < sizeof(type)) ? \ | |
55 | ((type) (v)) & (~(~(type) 0 << (sizeof(v) * CHAR_BIT))) : \ | |
56 | (type) (v); \ | |
57 | }) | |
58 | ||
6dc2ca62 | 59 | /* |
d79865b9 | 60 | * bt_bitfield_write - write integer to a bitfield in native endianness |
6dc2ca62 MD |
61 | * |
62 | * Save integer to the bitfield, which starts at the "start" bit, has "len" | |
63 | * bits. | |
64 | * The inside of a bitfield is from high bits to low bits. | |
65 | * Uses native endianness. | |
66 | * For unsigned "v", pad MSB with 0 if bitfield is larger than v. | |
67 | * For signed "v", sign-extend v if bitfield is larger than v. | |
68 | * | |
69 | * On little endian, bytes are placed from the less significant to the most | |
70 | * significant. Also, consecutive bitfields are placed from lower bits to higher | |
71 | * bits. | |
72 | * | |
73 | * On big endian, bytes are places from most significant to less significant. | |
74 | * Also, consecutive bitfields are placed from higher to lower bits. | |
75 | */ | |
76 | ||
d79865b9 | 77 | #define _bt_bitfield_write_le(ptr, _start, _length, _v) \ |
6dc2ca62 | 78 | do { \ |
08228826 | 79 | typeof(_v) v = (_v); \ |
6dc2ca62 | 80 | typeof(*(ptr)) mask, cmask; \ |
08228826 | 81 | unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \ |
6dc2ca62 | 82 | unsigned long start = (_start), length = (_length); \ |
6dc2ca62 MD |
83 | unsigned long start_unit, end_unit, this_unit; \ |
84 | unsigned long end, cshift; /* cshift is "complement shift" */ \ | |
6dc2ca62 MD |
85 | \ |
86 | if (!length) \ | |
87 | break; \ | |
88 | \ | |
89 | end = start + length; \ | |
90 | start_unit = start / ts; \ | |
91 | end_unit = (end + (ts - 1)) / ts; \ | |
92 | \ | |
93 | /* Trim v high bits */ \ | |
94 | if (length < sizeof(v) * CHAR_BIT) \ | |
95 | v &= ~((~(typeof(v)) 0) << length); \ | |
96 | \ | |
97 | /* We can now append v with a simple "or", shift it piece-wise */ \ | |
98 | this_unit = start_unit; \ | |
99 | if (start_unit == end_unit - 1) { \ | |
100 | mask = ~((~(typeof(*(ptr))) 0) << (start % ts)); \ | |
101 | if (end % ts) \ | |
102 | mask |= (~(typeof(*(ptr))) 0) << (end % ts); \ | |
103 | cmask = (typeof(*(ptr))) v << (start % ts); \ | |
08228826 | 104 | cmask &= ~mask; \ |
6dc2ca62 MD |
105 | (ptr)[this_unit] &= mask; \ |
106 | (ptr)[this_unit] |= cmask; \ | |
107 | break; \ | |
108 | } \ | |
109 | if (start % ts) { \ | |
110 | cshift = start % ts; \ | |
111 | mask = ~((~(typeof(*(ptr))) 0) << cshift); \ | |
112 | cmask = (typeof(*(ptr))) v << cshift; \ | |
08228826 | 113 | cmask &= ~mask; \ |
6dc2ca62 MD |
114 | (ptr)[this_unit] &= mask; \ |
115 | (ptr)[this_unit] |= cmask; \ | |
d79865b9 | 116 | v = _bt_piecewise_rshift(v, ts - cshift); \ |
6dc2ca62 MD |
117 | start += ts - cshift; \ |
118 | this_unit++; \ | |
119 | } \ | |
120 | for (; this_unit < end_unit - 1; this_unit++) { \ | |
121 | (ptr)[this_unit] = (typeof(*(ptr))) v; \ | |
d79865b9 | 122 | v = _bt_piecewise_rshift(v, ts); \ |
6dc2ca62 MD |
123 | start += ts; \ |
124 | } \ | |
125 | if (end % ts) { \ | |
126 | mask = (~(typeof(*(ptr))) 0) << (end % ts); \ | |
127 | cmask = (typeof(*(ptr))) v; \ | |
08228826 | 128 | cmask &= ~mask; \ |
6dc2ca62 MD |
129 | (ptr)[this_unit] &= mask; \ |
130 | (ptr)[this_unit] |= cmask; \ | |
131 | } else \ | |
132 | (ptr)[this_unit] = (typeof(*(ptr))) v; \ | |
133 | } while (0) | |
134 | ||
d79865b9 | 135 | #define _bt_bitfield_write_be(ptr, _start, _length, _v) \ |
6dc2ca62 MD |
136 | do { \ |
137 | typeof(_v) v = (_v); \ | |
08228826 MD |
138 | typeof(*(ptr)) mask, cmask; \ |
139 | unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \ | |
6dc2ca62 MD |
140 | unsigned long start = _start, length = _length; \ |
141 | unsigned long start_unit, end_unit, this_unit; \ | |
142 | unsigned long end, cshift; /* cshift is "complement shift" */ \ | |
6dc2ca62 MD |
143 | \ |
144 | if (!length) \ | |
145 | break; \ | |
146 | \ | |
147 | end = start + length; \ | |
148 | start_unit = start / ts; \ | |
149 | end_unit = (end + (ts - 1)) / ts; \ | |
150 | \ | |
151 | /* Trim v high bits */ \ | |
152 | if (length < sizeof(v) * CHAR_BIT) \ | |
153 | v &= ~((~(typeof(v)) 0) << length); \ | |
154 | \ | |
155 | /* We can now append v with a simple "or", shift it piece-wise */ \ | |
156 | this_unit = end_unit - 1; \ | |
157 | if (start_unit == end_unit - 1) { \ | |
158 | mask = ~((~(typeof(*(ptr))) 0) << ((ts - (end % ts)) % ts)); \ | |
159 | if (start % ts) \ | |
160 | mask |= (~((typeof(*(ptr))) 0)) << (ts - (start % ts)); \ | |
161 | cmask = (typeof(*(ptr))) v << ((ts - (end % ts)) % ts); \ | |
08228826 | 162 | cmask &= ~mask; \ |
6dc2ca62 MD |
163 | (ptr)[this_unit] &= mask; \ |
164 | (ptr)[this_unit] |= cmask; \ | |
165 | break; \ | |
166 | } \ | |
167 | if (end % ts) { \ | |
168 | cshift = end % ts; \ | |
169 | mask = ~((~(typeof(*(ptr))) 0) << (ts - cshift)); \ | |
170 | cmask = (typeof(*(ptr))) v << (ts - cshift); \ | |
08228826 | 171 | cmask &= ~mask; \ |
6dc2ca62 MD |
172 | (ptr)[this_unit] &= mask; \ |
173 | (ptr)[this_unit] |= cmask; \ | |
d79865b9 | 174 | v = _bt_piecewise_rshift(v, cshift); \ |
6dc2ca62 MD |
175 | end -= cshift; \ |
176 | this_unit--; \ | |
177 | } \ | |
08228826 | 178 | for (; (long) this_unit >= (long) start_unit + 1; this_unit--) { \ |
6dc2ca62 | 179 | (ptr)[this_unit] = (typeof(*(ptr))) v; \ |
d79865b9 | 180 | v = _bt_piecewise_rshift(v, ts); \ |
6dc2ca62 MD |
181 | end -= ts; \ |
182 | } \ | |
183 | if (start % ts) { \ | |
184 | mask = (~(typeof(*(ptr))) 0) << (ts - (start % ts)); \ | |
185 | cmask = (typeof(*(ptr))) v; \ | |
08228826 | 186 | cmask &= ~mask; \ |
6dc2ca62 MD |
187 | (ptr)[this_unit] &= mask; \ |
188 | (ptr)[this_unit] |= cmask; \ | |
189 | } else \ | |
190 | (ptr)[this_unit] = (typeof(*(ptr))) v; \ | |
191 | } while (0) | |
192 | ||
193 | /* | |
d79865b9 MD |
194 | * bt_bitfield_write - write integer to a bitfield in native endianness |
195 | * bt_bitfield_write_le - write integer to a bitfield in little endian | |
196 | * bt_bitfield_write_be - write integer to a bitfield in big endian | |
6dc2ca62 MD |
197 | */ |
198 | ||
199 | #if (BYTE_ORDER == LITTLE_ENDIAN) | |
200 | ||
d79865b9 MD |
201 | #define bt_bitfield_write(ptr, _start, _length, _v) \ |
202 | _bt_bitfield_write_le(ptr, _start, _length, _v) | |
6dc2ca62 | 203 | |
d79865b9 MD |
204 | #define bt_bitfield_write_le(ptr, _start, _length, _v) \ |
205 | _bt_bitfield_write_le(ptr, _start, _length, _v) | |
6dc2ca62 | 206 | |
d79865b9 MD |
207 | #define bt_bitfield_write_be(ptr, _start, _length, _v) \ |
208 | _bt_bitfield_write_be((unsigned char *) (ptr), _start, _length, _v) | |
6dc2ca62 MD |
209 | |
210 | #elif (BYTE_ORDER == BIG_ENDIAN) | |
211 | ||
d79865b9 MD |
212 | #define bt_bitfield_write(ptr, _start, _length, _v) \ |
213 | _bt_bitfield_write_be(ptr, _start, _length, _v) | |
6dc2ca62 | 214 | |
d79865b9 MD |
215 | #define bt_bitfield_write_le(ptr, _start, _length, _v) \ |
216 | _bt_bitfield_write_le((unsigned char *) (ptr), _start, _length, _v) | |
6dc2ca62 | 217 | |
d79865b9 MD |
218 | #define bt_bitfield_write_be(ptr, _start, _length, _v) \ |
219 | _bt_bitfield_write_be(ptr, _start, _length, _v) | |
6dc2ca62 MD |
220 | |
221 | #else /* (BYTE_ORDER == PDP_ENDIAN) */ | |
222 | ||
223 | #error "Byte order not supported" | |
224 | ||
225 | #endif | |
226 | ||
d79865b9 | 227 | #define _bt_bitfield_read_le(ptr, _start, _length, vptr) \ |
6dc2ca62 | 228 | do { \ |
08228826 MD |
229 | typeof(*(vptr)) v; \ |
230 | typeof(*(ptr)) mask, cmask; \ | |
231 | unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \ | |
6dc2ca62 MD |
232 | unsigned long start = _start, length = _length; \ |
233 | unsigned long start_unit, end_unit, this_unit; \ | |
234 | unsigned long end, cshift; /* cshift is "complement shift" */ \ | |
08228826 MD |
235 | \ |
236 | if (!length) { \ | |
237 | *(vptr) = 0; \ | |
238 | break; \ | |
239 | } \ | |
240 | \ | |
241 | end = start + length; \ | |
242 | start_unit = start / ts; \ | |
243 | end_unit = (end + (ts - 1)) / ts; \ | |
6a7b3345 MD |
244 | \ |
245 | this_unit = end_unit - 1; \ | |
246 | if (_bt_is_signed_type(typeof(v)) \ | |
08228826 | 247 | && ((ptr)[this_unit] & ((typeof(*(ptr))) 1 << ((end % ts ? : ts) - 1)))) \ |
6a7b3345 MD |
248 | v = ~(typeof(v)) 0; \ |
249 | else \ | |
250 | v = 0; \ | |
251 | if (start_unit == end_unit - 1) { \ | |
252 | cmask = (ptr)[this_unit]; \ | |
253 | cmask >>= (start % ts); \ | |
254 | if ((end - start) % ts) { \ | |
08228826 | 255 | mask = ~((~(typeof(*(ptr))) 0) << (end - start)); \ |
6a7b3345 MD |
256 | cmask &= mask; \ |
257 | } \ | |
258 | v = _bt_piecewise_lshift(v, end - start); \ | |
259 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
260 | *(vptr) = v; \ | |
261 | break; \ | |
262 | } \ | |
263 | if (end % ts) { \ | |
264 | cshift = end % ts; \ | |
265 | mask = ~((~(typeof(*(ptr))) 0) << cshift); \ | |
266 | cmask = (ptr)[this_unit]; \ | |
267 | cmask &= mask; \ | |
268 | v = _bt_piecewise_lshift(v, cshift); \ | |
269 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
270 | end -= cshift; \ | |
271 | this_unit--; \ | |
272 | } \ | |
08228826 | 273 | for (; (long) this_unit >= (long) start_unit + 1; this_unit--) { \ |
6a7b3345 MD |
274 | v = _bt_piecewise_lshift(v, ts); \ |
275 | v |= _bt_unsigned_cast(typeof(v), (ptr)[this_unit]); \ | |
276 | end -= ts; \ | |
277 | } \ | |
278 | if (start % ts) { \ | |
279 | mask = ~((~(typeof(*(ptr))) 0) << (ts - (start % ts))); \ | |
280 | cmask = (ptr)[this_unit]; \ | |
281 | cmask >>= (start % ts); \ | |
282 | cmask &= mask; \ | |
283 | v = _bt_piecewise_lshift(v, ts - (start % ts)); \ | |
284 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
285 | } else { \ | |
286 | v = _bt_piecewise_lshift(v, ts); \ | |
287 | v |= _bt_unsigned_cast(typeof(v), (ptr)[this_unit]); \ | |
288 | } \ | |
289 | *(vptr) = v; \ | |
08228826 MD |
290 | } while (0) |
291 | ||
d79865b9 | 292 | #define _bt_bitfield_read_be(ptr, _start, _length, vptr) \ |
08228826 MD |
293 | do { \ |
294 | typeof(*(vptr)) v; \ | |
6dc2ca62 MD |
295 | typeof(*(ptr)) mask, cmask; \ |
296 | unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \ | |
08228826 MD |
297 | unsigned long start = _start, length = _length; \ |
298 | unsigned long start_unit, end_unit, this_unit; \ | |
299 | unsigned long end, cshift; /* cshift is "complement shift" */ \ | |
6dc2ca62 | 300 | \ |
08228826 MD |
301 | if (!length) { \ |
302 | *(vptr) = 0; \ | |
6dc2ca62 | 303 | break; \ |
08228826 | 304 | } \ |
6dc2ca62 MD |
305 | \ |
306 | end = start + length; \ | |
307 | start_unit = start / ts; \ | |
308 | end_unit = (end + (ts - 1)) / ts; \ | |
6a7b3345 | 309 | \ |
08228826 | 310 | this_unit = start_unit; \ |
d79865b9 | 311 | if (_bt_is_signed_type(typeof(v)) \ |
08228826 | 312 | && ((ptr)[this_unit] & ((typeof(*(ptr))) 1 << (ts - (start % ts) - 1)))) \ |
6a7b3345 MD |
313 | v = ~(typeof(v)) 0; \ |
314 | else \ | |
315 | v = 0; \ | |
316 | if (start_unit == end_unit - 1) { \ | |
317 | cmask = (ptr)[this_unit]; \ | |
318 | cmask >>= (ts - (end % ts)) % ts; \ | |
319 | if ((end - start) % ts) { \ | |
08228826 | 320 | mask = ~((~(typeof(*(ptr))) 0) << (end - start)); \ |
6a7b3345 MD |
321 | cmask &= mask; \ |
322 | } \ | |
323 | v = _bt_piecewise_lshift(v, end - start); \ | |
324 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
325 | *(vptr) = v; \ | |
326 | break; \ | |
327 | } \ | |
328 | if (start % ts) { \ | |
329 | mask = ~((~(typeof(*(ptr))) 0) << (ts - (start % ts))); \ | |
330 | cmask = (ptr)[this_unit]; \ | |
331 | cmask &= mask; \ | |
332 | v = _bt_piecewise_lshift(v, ts - (start % ts)); \ | |
333 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
334 | start += ts - (start % ts); \ | |
335 | this_unit++; \ | |
336 | } \ | |
337 | for (; this_unit < end_unit - 1; this_unit++) { \ | |
338 | v = _bt_piecewise_lshift(v, ts); \ | |
339 | v |= _bt_unsigned_cast(typeof(v), (ptr)[this_unit]); \ | |
340 | start += ts; \ | |
341 | } \ | |
342 | if (end % ts) { \ | |
343 | mask = ~((~(typeof(*(ptr))) 0) << (end % ts)); \ | |
344 | cmask = (ptr)[this_unit]; \ | |
345 | cmask >>= ts - (end % ts); \ | |
346 | cmask &= mask; \ | |
347 | v = _bt_piecewise_lshift(v, end % ts); \ | |
348 | v |= _bt_unsigned_cast(typeof(v), cmask); \ | |
349 | } else { \ | |
350 | v = _bt_piecewise_lshift(v, ts); \ | |
351 | v |= _bt_unsigned_cast(typeof(v), (ptr)[this_unit]); \ | |
352 | } \ | |
353 | *(vptr) = v; \ | |
6dc2ca62 MD |
354 | } while (0) |
355 | ||
6dc2ca62 | 356 | /* |
d79865b9 MD |
357 | * bt_bitfield_read - read integer from a bitfield in native endianness |
358 | * bt_bitfield_read_le - read integer from a bitfield in little endian | |
359 | * bt_bitfield_read_be - read integer from a bitfield in big endian | |
6dc2ca62 MD |
360 | */ |
361 | ||
08228826 MD |
362 | #if (BYTE_ORDER == LITTLE_ENDIAN) |
363 | ||
d79865b9 MD |
364 | #define bt_bitfield_read(ptr, _start, _length, _vptr) \ |
365 | _bt_bitfield_read_le(ptr, _start, _length, _vptr) | |
08228826 | 366 | |
d79865b9 MD |
367 | #define bt_bitfield_read_le(ptr, _start, _length, _vptr) \ |
368 | _bt_bitfield_read_le(ptr, _start, _length, _vptr) | |
08228826 | 369 | |
d79865b9 MD |
370 | #define bt_bitfield_read_be(ptr, _start, _length, _vptr) \ |
371 | _bt_bitfield_read_be((const unsigned char *) (ptr), _start, _length, _vptr) | |
08228826 MD |
372 | |
373 | #elif (BYTE_ORDER == BIG_ENDIAN) | |
374 | ||
d79865b9 MD |
375 | #define bt_bitfield_read(ptr, _start, _length, _vptr) \ |
376 | _bt_bitfield_read_be(ptr, _start, _length, _vptr) | |
08228826 | 377 | |
d79865b9 MD |
378 | #define bt_bitfield_read_le(ptr, _start, _length, _vptr) \ |
379 | _bt_bitfield_read_le((const unsigned char *) (ptr), _start, _length, _vptr) | |
08228826 | 380 | |
d79865b9 MD |
381 | #define bt_bitfield_read_be(ptr, _start, _length, _vptr) \ |
382 | _bt_bitfield_read_be(ptr, _start, _length, _vptr) | |
08228826 MD |
383 | |
384 | #else /* (BYTE_ORDER == PDP_ENDIAN) */ | |
385 | ||
386 | #error "Byte order not supported" | |
387 | ||
388 | #endif | |
6dc2ca62 | 389 | |
d79865b9 | 390 | #endif /* _BABELTRACE_BITFIELD_H */ |