40c1161489d66d6d56cba1fe1ba1803973289b18
[babeltrace.git] / include / babeltrace / bitfield.h
1 #ifndef _BABELTRACE_BITFIELD_H
2 #define _BABELTRACE_BITFIELD_H
3
4 /*
5  * BabelTrace
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  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE.
28  */
29
30 #include <stdint.h>     /* C99 5.2.4.2 Numerical limits */
31 #include <limits.h>     /* C99 5.2.4.2 Numerical limits */
32 #include <assert.h>
33 #include <babeltrace/endian.h>  /* Non-standard BIG_ENDIAN, LITTLE_ENDIAN, BYTE_ORDER */
34
35 /* We can't shift a int from 32 bit, >> 32 and << 32 on int is undefined */
36 #define _bt_piecewise_rshift(_v, _shift)                                \
37 ({                                                                      \
38         typeof(_v) ___v = (_v);                                         \
39         typeof(_shift) ___shift = (_shift);                             \
40         unsigned long sb = (___shift) / (sizeof(___v) * CHAR_BIT - 1);  \
41         unsigned long final = (___shift) % (sizeof(___v) * CHAR_BIT - 1); \
42                                                                         \
43         for (; sb; sb--)                                                \
44                 ___v >>= sizeof(___v) * CHAR_BIT - 1;                   \
45         ___v >>= final;                                                 \
46 })
47
48 #define _bt_piecewise_lshift(_v, _shift)                                \
49 ({                                                                      \
50         typeof(_v) ___v = (_v);                                         \
51         typeof(_shift) ___shift = (_shift);                             \
52         unsigned long sb = (___shift) / (sizeof(___v) * CHAR_BIT - 1);  \
53         unsigned long final = (___shift) % (sizeof(___v) * CHAR_BIT - 1); \
54                                                                         \
55         for (; sb; sb--)                                                \
56                 ___v <<= sizeof(___v) * CHAR_BIT - 1;                   \
57         ___v <<= final;                                                 \
58 })
59
60 #define _bt_is_signed_type(type)        (((type)(-1)) < 0)
61
62 #define _bt_unsigned_cast(type, v)                                      \
63 ({                                                                      \
64         (sizeof(v) < sizeof(type)) ?                                    \
65                 ((type) (v)) & (~(~(type) 0 << (sizeof(v) * CHAR_BIT))) : \
66                 (type) (v);                                             \
67 })
68
69 /*
70  * bt_bitfield_write - write integer to a bitfield in native endianness
71  *
72  * Save integer to the bitfield, which starts at the "start" bit, has "len"
73  * bits.
74  * The inside of a bitfield is from high bits to low bits.
75  * Uses native endianness.
76  * For unsigned "v", pad MSB with 0 if bitfield is larger than v.
77  * For signed "v", sign-extend v if bitfield is larger than v.
78  *
79  * On little endian, bytes are placed from the less significant to the most
80  * significant. Also, consecutive bitfields are placed from lower bits to higher
81  * bits.
82  *
83  * On big endian, bytes are places from most significant to less significant.
84  * Also, consecutive bitfields are placed from higher to lower bits.
85  */
86
87 #define _bt_bitfield_write_le(_ptr, type, _start, _length, _v)          \
88 do {                                                                    \
89         typeof(_v) __v = (_v);                                          \
90         type *__ptr = (void *) (_ptr);                                  \
91         unsigned long __start = (_start), __length = (_length);         \
92         type mask, cmask;                                               \
93         unsigned long ts = sizeof(type) * CHAR_BIT; /* type size */     \
94         unsigned long start_unit, end_unit, this_unit;                  \
95         unsigned long end, cshift; /* cshift is "complement shift" */   \
96                                                                         \
97         if (!__length)                                                  \
98                 break;                                                  \
99                                                                         \
100         end = __start + __length;                                       \
101         start_unit = __start / ts;                                      \
102         end_unit = (end + (ts - 1)) / ts;                               \
103                                                                         \
104         /* Trim v high bits */                                          \
105         if (__length < sizeof(__v) * CHAR_BIT)                          \
106                 __v &= ~((~(typeof(__v)) 0) << __length);               \
107                                                                         \
108         /* We can now append v with a simple "or", shift it piece-wise */ \
109         this_unit = start_unit;                                         \
110         if (start_unit == end_unit - 1) {                               \
111                 mask = ~((~(type) 0) << (__start % ts));                \
112                 if (end % ts)                                           \
113                         mask |= (~(type) 0) << (end % ts);              \
114                 cmask = (type) __v << (__start % ts);                   \
115                 cmask &= ~mask;                                         \
116                 __ptr[this_unit] &= mask;                               \
117                 __ptr[this_unit] |= cmask;                              \
118                 break;                                                  \
119         }                                                               \
120         if (__start % ts) {                                             \
121                 cshift = __start % ts;                                  \
122                 mask = ~((~(type) 0) << cshift);                        \
123                 cmask = (type) __v << cshift;                           \
124                 cmask &= ~mask;                                         \
125                 __ptr[this_unit] &= mask;                               \
126                 __ptr[this_unit] |= cmask;                              \
127                 __v = _bt_piecewise_rshift(__v, ts - cshift);           \
128                 __start += ts - cshift;                                 \
129                 this_unit++;                                            \
130         }                                                               \
131         for (; this_unit < end_unit - 1; this_unit++) {                 \
132                 __ptr[this_unit] = (type) __v;                          \
133                 __v = _bt_piecewise_rshift(__v, ts);                    \
134                 __start += ts;                                          \
135         }                                                               \
136         if (end % ts) {                                                 \
137                 mask = (~(type) 0) << (end % ts);                       \
138                 cmask = (type) __v;                                     \
139                 cmask &= ~mask;                                         \
140                 __ptr[this_unit] &= mask;                               \
141                 __ptr[this_unit] |= cmask;                              \
142         } else                                                          \
143                 __ptr[this_unit] = (type) __v;                          \
144 } while (0)
145
146 #define _bt_bitfield_write_be(_ptr, type, _start, _length, _v)          \
147 do {                                                                    \
148         typeof(_v) __v = (_v);                                          \
149         type *__ptr = (void *) (_ptr);                                  \
150         unsigned long __start = (_start), __length = (_length);         \
151         type mask, cmask;                                               \
152         unsigned long ts = sizeof(type) * CHAR_BIT; /* type size */     \
153         unsigned long start_unit, end_unit, this_unit;                  \
154         unsigned long end, cshift; /* cshift is "complement shift" */   \
155                                                                         \
156         if (!__length)                                                  \
157                 break;                                                  \
158                                                                         \
159         end = __start + __length;                                       \
160         start_unit = __start / ts;                                      \
161         end_unit = (end + (ts - 1)) / ts;                               \
162                                                                         \
163         /* Trim v high bits */                                          \
164         if (__length < sizeof(__v) * CHAR_BIT)                          \
165                 __v &= ~((~(typeof(__v)) 0) << __length);               \
166                                                                         \
167         /* We can now append v with a simple "or", shift it piece-wise */ \
168         this_unit = end_unit - 1;                                       \
169         if (start_unit == end_unit - 1) {                               \
170                 mask = ~((~(type) 0) << ((ts - (end % ts)) % ts));      \
171                 if (__start % ts)                                       \
172                         mask |= (~((type) 0)) << (ts - (__start % ts)); \
173                 cmask = (type) __v << ((ts - (end % ts)) % ts);         \
174                 cmask &= ~mask;                                         \
175                 __ptr[this_unit] &= mask;                               \
176                 __ptr[this_unit] |= cmask;                              \
177                 break;                                                  \
178         }                                                               \
179         if (end % ts) {                                                 \
180                 cshift = end % ts;                                      \
181                 mask = ~((~(type) 0) << (ts - cshift));                 \
182                 cmask = (type) __v << (ts - cshift);                    \
183                 cmask &= ~mask;                                         \
184                 __ptr[this_unit] &= mask;                               \
185                 __ptr[this_unit] |= cmask;                              \
186                 __v = _bt_piecewise_rshift(__v, cshift);                \
187                 end -= cshift;                                          \
188                 this_unit--;                                            \
189         }                                                               \
190         for (; (long) this_unit >= (long) start_unit + 1; this_unit--) { \
191                 __ptr[this_unit] = (type) __v;                          \
192                 __v = _bt_piecewise_rshift(__v, ts);                    \
193                 end -= ts;                                              \
194         }                                                               \
195         if (__start % ts) {                                             \
196                 mask = (~(type) 0) << (ts - (__start % ts));            \
197                 cmask = (type) __v;                                     \
198                 cmask &= ~mask;                                         \
199                 __ptr[this_unit] &= mask;                               \
200                 __ptr[this_unit] |= cmask;                              \
201         } else                                                          \
202                 __ptr[this_unit] = (type) __v;                          \
203 } while (0)
204
205 /*
206  * bt_bitfield_write - write integer to a bitfield in native endianness
207  * bt_bitfield_write_le - write integer to a bitfield in little endian
208  * bt_bitfield_write_be - write integer to a bitfield in big endian
209  */
210
211 #if (BYTE_ORDER == LITTLE_ENDIAN)
212
213 #define bt_bitfield_write(ptr, type, _start, _length, _v)               \
214         _bt_bitfield_write_le(ptr, type, _start, _length, _v)
215
216 #define bt_bitfield_write_le(ptr, type, _start, _length, _v)            \
217         _bt_bitfield_write_le(ptr, type, _start, _length, _v)
218         
219 #define bt_bitfield_write_be(ptr, type, _start, _length, _v)            \
220         _bt_bitfield_write_be(ptr, unsigned char, _start, _length, _v)
221
222 #elif (BYTE_ORDER == BIG_ENDIAN)
223
224 #define bt_bitfield_write(ptr, type, _start, _length, _v)               \
225         _bt_bitfield_write_be(ptr, type, _start, _length, _v)
226
227 #define bt_bitfield_write_le(ptr, type, _start, _length, _v)            \
228         _bt_bitfield_write_le(ptr, unsigned char, _start, _length, _v)
229         
230 #define bt_bitfield_write_be(ptr, type, _start, _length, _v)            \
231         _bt_bitfield_write_be(ptr, type, _start, _length, _v)
232
233 #else /* (BYTE_ORDER == PDP_ENDIAN) */
234
235 #error "Byte order not supported"
236
237 #endif
238
239 #define _bt_bitfield_read_le(_ptr, type, _start, _length, _vptr)        \
240 do {                                                                    \
241         typeof(*(_vptr)) *__vptr = (_vptr);                             \
242         typeof(*__vptr) __v;                                            \
243         type *__ptr = (void *) (_ptr);                                  \
244         unsigned long __start = (_start), __length = (_length);         \
245         type mask, cmask;                                               \
246         unsigned long ts = sizeof(type) * CHAR_BIT; /* type size */     \
247         unsigned long start_unit, end_unit, this_unit;                  \
248         unsigned long end, cshift; /* cshift is "complement shift" */   \
249                                                                         \
250         if (!__length) {                                                \
251                 *__vptr = 0;                                            \
252                 break;                                                  \
253         }                                                               \
254                                                                         \
255         end = __start + __length;                                       \
256         start_unit = __start / ts;                                      \
257         end_unit = (end + (ts - 1)) / ts;                               \
258                                                                         \
259         this_unit = end_unit - 1;                                       \
260         if (_bt_is_signed_type(typeof(__v))                             \
261             && (__ptr[this_unit] & ((type) 1 << ((end % ts ? : ts) - 1)))) \
262                 __v = ~(typeof(__v)) 0;                                 \
263         else                                                            \
264                 __v = 0;                                                \
265         if (start_unit == end_unit - 1) {                               \
266                 cmask = __ptr[this_unit];                               \
267                 cmask >>= (__start % ts);                               \
268                 if ((end - __start) % ts) {                             \
269                         mask = ~((~(type) 0) << (end - __start));       \
270                         cmask &= mask;                                  \
271                 }                                                       \
272                 __v = _bt_piecewise_lshift(__v, end - __start);         \
273                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
274                 *__vptr = __v;                                          \
275                 break;                                                  \
276         }                                                               \
277         if (end % ts) {                                                 \
278                 cshift = end % ts;                                      \
279                 mask = ~((~(type) 0) << cshift);                        \
280                 cmask = __ptr[this_unit];                               \
281                 cmask &= mask;                                          \
282                 __v = _bt_piecewise_lshift(__v, cshift);                \
283                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
284                 end -= cshift;                                          \
285                 this_unit--;                                            \
286         }                                                               \
287         for (; (long) this_unit >= (long) start_unit + 1; this_unit--) { \
288                 __v = _bt_piecewise_lshift(__v, ts);                    \
289                 __v |= _bt_unsigned_cast(typeof(__v), __ptr[this_unit]);\
290                 end -= ts;                                              \
291         }                                                               \
292         if (__start % ts) {                                             \
293                 mask = ~((~(type) 0) << (ts - (__start % ts)));         \
294                 cmask = __ptr[this_unit];                               \
295                 cmask >>= (__start % ts);                               \
296                 cmask &= mask;                                          \
297                 __v = _bt_piecewise_lshift(__v, ts - (__start % ts));   \
298                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
299         } else {                                                        \
300                 __v = _bt_piecewise_lshift(__v, ts);                    \
301                 __v |= _bt_unsigned_cast(typeof(__v), __ptr[this_unit]);\
302         }                                                               \
303         *__vptr = __v;                                                  \
304 } while (0)
305
306 #define _bt_bitfield_read_be(_ptr, type, _start, _length, _vptr)        \
307 do {                                                                    \
308         typeof(*(_vptr)) *__vptr = (_vptr);                             \
309         typeof(*__vptr) __v;                                            \
310         type *__ptr = (void *) (_ptr);                                  \
311         unsigned long __start = (_start), __length = (_length);         \
312         type mask, cmask;                                               \
313         unsigned long ts = sizeof(type) * CHAR_BIT; /* type size */     \
314         unsigned long start_unit, end_unit, this_unit;                  \
315         unsigned long end, cshift; /* cshift is "complement shift" */   \
316                                                                         \
317         if (!__length) {                                                \
318                 *__vptr = 0;                                            \
319                 break;                                                  \
320         }                                                               \
321                                                                         \
322         end = __start + __length;                                       \
323         start_unit = __start / ts;                                      \
324         end_unit = (end + (ts - 1)) / ts;                               \
325                                                                         \
326         this_unit = start_unit;                                         \
327         if (_bt_is_signed_type(typeof(__v))                             \
328             && (__ptr[this_unit] & ((type) 1 << (ts - (__start % ts) - 1)))) \
329                 __v = ~(typeof(__v)) 0;                                 \
330         else                                                            \
331                 __v = 0;                                                \
332         if (start_unit == end_unit - 1) {                               \
333                 cmask = __ptr[this_unit];                               \
334                 cmask >>= (ts - (end % ts)) % ts;                       \
335                 if ((end - __start) % ts) {                             \
336                         mask = ~((~(type) 0) << (end - __start));       \
337                         cmask &= mask;                                  \
338                 }                                                       \
339                 __v = _bt_piecewise_lshift(__v, end - __start);         \
340                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
341                 *__vptr = __v;                                          \
342                 break;                                                  \
343         }                                                               \
344         if (__start % ts) {                                             \
345                 cshift = __start % ts;                                  \
346                 mask = ~((~(type) 0) << (ts - cshift));                 \
347                 cmask = __ptr[this_unit];                               \
348                 cmask &= mask;                                          \
349                 __v = _bt_piecewise_lshift(__v, ts - cshift);           \
350                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
351                 __start += ts - cshift;                                 \
352                 this_unit++;                                            \
353         }                                                               \
354         for (; this_unit < end_unit - 1; this_unit++) {                 \
355                 __v = _bt_piecewise_lshift(__v, ts);                    \
356                 __v |= _bt_unsigned_cast(typeof(__v), __ptr[this_unit]);\
357                 __start += ts;                                          \
358         }                                                               \
359         if (end % ts) {                                                 \
360                 mask = ~((~(type) 0) << (end % ts));                    \
361                 cmask = __ptr[this_unit];                               \
362                 cmask >>= ts - (end % ts);                              \
363                 cmask &= mask;                                          \
364                 __v = _bt_piecewise_lshift(__v, end % ts);              \
365                 __v |= _bt_unsigned_cast(typeof(__v), cmask);           \
366         } else {                                                        \
367                 __v = _bt_piecewise_lshift(__v, ts);                    \
368                 __v |= _bt_unsigned_cast(typeof(__v), __ptr[this_unit]);\
369         }                                                               \
370         *__vptr = __v;                                                  \
371 } while (0)
372
373 /*
374  * bt_bitfield_read - read integer from a bitfield in native endianness
375  * bt_bitfield_read_le - read integer from a bitfield in little endian
376  * bt_bitfield_read_be - read integer from a bitfield in big endian
377  */
378
379 #if (BYTE_ORDER == LITTLE_ENDIAN)
380
381 #define bt_bitfield_read(_ptr, type, _start, _length, _vptr)            \
382         _bt_bitfield_read_le(_ptr, type, _start, _length, _vptr)
383
384 #define bt_bitfield_read_le(_ptr, type, _start, _length, _vptr)         \
385         _bt_bitfield_read_le(_ptr, type, _start, _length, _vptr)
386         
387 #define bt_bitfield_read_be(_ptr, type, _start, _length, _vptr)         \
388         _bt_bitfield_read_be(_ptr, unsigned char, _start, _length, _vptr)
389
390 #elif (BYTE_ORDER == BIG_ENDIAN)
391
392 #define bt_bitfield_read(_ptr, type, _start, _length, _vptr)            \
393         _bt_bitfield_read_be(_ptr, type, _start, _length, _vptr)
394
395 #define bt_bitfield_read_le(_ptr, type, _start, _length, _vptr)         \
396         _bt_bitfield_read_le(_ptr, unsigned char, _start, _length, _vptr)
397         
398 #define bt_bitfield_read_be(_ptr, type, _start, _length, _vptr)         \
399         _bt_bitfield_read_be(_ptr, type, _start, _length, _vptr)
400
401 #else /* (BYTE_ORDER == PDP_ENDIAN) */
402
403 #error "Byte order not supported"
404
405 #endif
406
407 #endif /* _BABELTRACE_BITFIELD_H */