Common Trace Format - initial commit
[babeltrace.git] / include / ctf / bitfield.h
1 #ifndef _CTF_BITFIELD_H
2 #define _CTF_BITFIELD_H
3
4 /*
5 * Common Trace Format
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
27 /* We can't shift a int from 32 bit, >> 32 on int is undefined */
28 #define _ctf_piecewise_rshift(v, shift) \
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
39 /*
40 * ctf_bitfield_write - write integer to a bitfield in native endianness
41 *
42 * Save integer to the bitfield, which starts at the "start" bit, has "len"
43 * bits.
44 * The inside of a bitfield is from high bits to low bits.
45 * Uses native endianness.
46 * For unsigned "v", pad MSB with 0 if bitfield is larger than v.
47 * For signed "v", sign-extend v if bitfield is larger than v.
48 *
49 * On little endian, bytes are placed from the less significant to the most
50 * significant. Also, consecutive bitfields are placed from lower bits to higher
51 * bits.
52 *
53 * On big endian, bytes are places from most significant to less significant.
54 * Also, consecutive bitfields are placed from higher to lower bits.
55 */
56
57 #define _ctf_bitfield_write_le(ptr, _start, _length, _v) \
58 do { \
59 typeof(*(ptr)) mask, cmask; \
60 unsigned long start = (_start), length = (_length); \
61 typeof(_v) v = (_v); \
62 unsigned long start_unit, end_unit, this_unit; \
63 unsigned long end, cshift; /* cshift is "complement shift" */ \
64 unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \
65 \
66 if (!length) \
67 break; \
68 \
69 end = start + length; \
70 start_unit = start / ts; \
71 end_unit = (end + (ts - 1)) / ts; \
72 \
73 /* Trim v high bits */ \
74 if (length < sizeof(v) * CHAR_BIT) \
75 v &= ~((~(typeof(v)) 0) << length); \
76 \
77 /* We can now append v with a simple "or", shift it piece-wise */ \
78 this_unit = start_unit; \
79 if (start_unit == end_unit - 1) { \
80 mask = ~((~(typeof(*(ptr))) 0) << (start % ts)); \
81 if (end % ts) \
82 mask |= (~(typeof(*(ptr))) 0) << (end % ts); \
83 cmask = (typeof(*(ptr))) v << (start % ts); \
84 (ptr)[this_unit] &= mask; \
85 (ptr)[this_unit] |= cmask; \
86 break; \
87 } \
88 if (start % ts) { \
89 cshift = start % ts; \
90 mask = ~((~(typeof(*(ptr))) 0) << cshift); \
91 cmask = (typeof(*(ptr))) v << cshift; \
92 (ptr)[this_unit] &= mask; \
93 (ptr)[this_unit] |= cmask; \
94 v = _ctf_piecewise_rshift(v, ts - cshift); \
95 start += ts - cshift; \
96 this_unit++; \
97 } \
98 for (; this_unit < end_unit - 1; this_unit++) { \
99 (ptr)[this_unit] = (typeof(*(ptr))) v; \
100 v = _ctf_piecewise_rshift(v, ts); \
101 start += ts; \
102 } \
103 if (end % ts) { \
104 mask = (~(typeof(*(ptr))) 0) << (end % ts); \
105 cmask = (typeof(*(ptr))) v; \
106 (ptr)[this_unit] &= mask; \
107 (ptr)[this_unit] |= cmask; \
108 } else \
109 (ptr)[this_unit] = (typeof(*(ptr))) v; \
110 } while (0)
111
112 #define _ctf_bitfield_write_be(ptr, _start, _length, _v) \
113 do { \
114 typeof(_v) v = (_v); \
115 unsigned long start = _start, length = _length; \
116 unsigned long start_unit, end_unit, this_unit; \
117 unsigned long end, cshift; /* cshift is "complement shift" */ \
118 typeof(*(ptr)) mask, cmask; \
119 unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \
120 \
121 if (!length) \
122 break; \
123 \
124 end = start + length; \
125 start_unit = start / ts; \
126 end_unit = (end + (ts - 1)) / ts; \
127 \
128 /* Trim v high bits */ \
129 if (length < sizeof(v) * CHAR_BIT) \
130 v &= ~((~(typeof(v)) 0) << length); \
131 \
132 /* We can now append v with a simple "or", shift it piece-wise */ \
133 this_unit = end_unit - 1; \
134 if (start_unit == end_unit - 1) { \
135 mask = ~((~(typeof(*(ptr))) 0) << ((ts - (end % ts)) % ts)); \
136 if (start % ts) \
137 mask |= (~((typeof(*(ptr))) 0)) << (ts - (start % ts)); \
138 cmask = (typeof(*(ptr))) v << ((ts - (end % ts)) % ts); \
139 (ptr)[this_unit] &= mask; \
140 (ptr)[this_unit] |= cmask; \
141 break; \
142 } \
143 if (end % ts) { \
144 cshift = end % ts; \
145 mask = ~((~(typeof(*(ptr))) 0) << (ts - cshift)); \
146 cmask = (typeof(*(ptr))) v << (ts - cshift); \
147 (ptr)[this_unit] &= mask; \
148 (ptr)[this_unit] |= cmask; \
149 v = _ctf_piecewise_rshift(v, cshift); \
150 end -= cshift; \
151 this_unit--; \
152 } \
153 for (; (long)this_unit >= (long)start_unit + 1; this_unit--) { \
154 (ptr)[this_unit] = (typeof(*(ptr))) v; \
155 v = _ctf_piecewise_rshift(v, ts); \
156 end -= ts; \
157 } \
158 if (start % ts) { \
159 mask = (~(typeof(*(ptr))) 0) << (ts - (start % ts)); \
160 cmask = (typeof(*(ptr))) v; \
161 (ptr)[this_unit] &= mask; \
162 (ptr)[this_unit] |= cmask; \
163 } else \
164 (ptr)[this_unit] = (typeof(*(ptr))) v; \
165 } while (0)
166
167 /*
168 * ctf_bitfield_write - write integer to a bitfield in native endianness
169 * ctf_bitfield_write_le - write integer to a bitfield in little endian
170 * ctf_bitfield_write_be - write integer to a bitfield in big endian
171 */
172
173 #if (BYTE_ORDER == LITTLE_ENDIAN)
174
175 #define ctf_bitfield_write(ptr, _start, _length, _v) \
176 _ctf_bitfield_write_le(ptr, _start, _length, _v)
177
178 #define ctf_bitfield_write_le(ptr, _start, _length, _v) \
179 _ctf_bitfield_write_le(ptr, _start, _length, _v)
180
181 #define ctf_bitfield_write_be(ptr, _start, _length, _v) \
182 _ctf_bitfield_write_be((uint8_t *) (ptr), _start, _length, _v)
183
184 #elif (BYTE_ORDER == BIG_ENDIAN)
185
186 #define ctf_bitfield_write(ptr, _start, _length, _v) \
187 _ctf_bitfield_write_be(ptr, _start, _length, _v)
188
189 #define ctf_bitfield_write_le(ptr, _start, _length, _v) \
190 _ctf_bitfield_write_le((uint8_t *) (ptr), _start, _length, _v)
191
192 #define ctf_bitfield_write_be(ptr, _start, _length, _v) \
193 _ctf_bitfield_write_be(ptr, _start, _length, _v)
194
195 #else /* (BYTE_ORDER == PDP_ENDIAN) */
196
197 #error "Byte order not supported"
198
199 #endif
200
201 #if 0
202 #define _ctf_bitfield_read_le(ptr, _start, _length, _vptr) \
203 do { \
204 typeof(_vptr) vptr = (_vptr); \
205 unsigned long start = _start, length = _length; \
206 unsigned long start_unit, end_unit, this_unit; \
207 unsigned long end, cshift; /* cshift is "complement shift" */ \
208 typeof(*(ptr)) mask, cmask; \
209 unsigned long ts = sizeof(typeof(*(ptr))) * CHAR_BIT; /* type size */ \
210 \
211 if (!length) \
212 break; \
213 \
214 end = start + length; \
215 start_unit = start / ts; \
216 end_unit = (end + (ts - 1)) / ts; \
217 } while (0)
218
219 #endif //0
220
221 /*
222 * Read a bitfield byte-wise. This function is arch-agnostic.
223 */
224
225 static inline
226 uint64_t _ctf_bitfield_read_64(const unsigned char *ptr,
227 unsigned long start, unsigned long len,
228 int byte_order, int signedness)
229 {
230 long start_unit, end_unit, this_unit;
231 unsigned long end, cshift; /* cshift is "complement shift" */
232 unsigned long ts = sizeof(unsigned char) * CHAR_BIT;
233 unsigned char mask, cmask;
234 uint64_t v = 0;
235
236 if (!len)
237 return 0;
238 end = start + len;
239 start_unit = start / ts;
240 end_unit = (end + (ts - 1)) / ts;
241
242 /*
243 * We can now fill v piece-wise, from lower bits to upper bits.
244 * We read the bitfield in the opposite direction it was written.
245 */
246 switch (byte_order) {
247 case LITTLE_ENDIAN:
248 this_unit = end_unit - 1;
249 if (signedness) {
250 if (ptr[this_unit] & (1U << ((end % ts ? : ts) - 1)))
251 v = ~(uint64_t) 0;
252 }
253 if (start_unit == end_unit - 1) {
254 mask = (~(unsigned char) 0) << (end % ts ? : ts);
255 mask |= ~((~(unsigned char) 0) << (start % ts));
256 cmask = ptr[this_unit];
257 cmask &= ~mask;
258 cmask >>= (start % ts);
259 v <<= end - start;
260 v |= cmask;
261 break;
262 }
263 if (end % ts) {
264 cshift = (end % ts ? : ts);
265 mask = (~(unsigned char) 0) << cshift;
266 cmask = ptr[this_unit];
267 cmask &= ~mask;
268 v <<= cshift;
269 v |= (uint64_t) cmask;
270 end -= cshift;
271 this_unit--;
272 }
273 for (; this_unit >= start_unit + 1; this_unit--) {
274 v <<= ts;
275 v |= (uint64_t) ptr[this_unit];
276 end -= ts;
277 }
278 if (start % ts) {
279 cmask = ptr[this_unit] >> (start % ts);
280 v <<= ts - (start % ts);
281 v |= (uint64_t) cmask;
282 } else {
283 v <<= ts;
284 v |= (uint64_t) ptr[this_unit];
285 }
286 break;
287 case BIG_ENDIAN:
288 this_unit = start_unit;
289 if (signedness) {
290 if (ptr[this_unit] & (1U << (ts - (start % ts) - 1)))
291 v = ~(uint64_t) 0;
292 }
293 if (start_unit == end_unit - 1) {
294 mask = (~(unsigned char) 0) << (ts - (start % ts));
295 mask |= ~((~(unsigned char) 0) << ((ts - (end % ts)) % ts));
296 cmask = ptr[this_unit];
297 cmask &= ~mask;
298 cmask >>= (ts - (end % ts)) % ts;
299 v <<= end - start;
300 v |= (uint64_t) cmask;
301 break;
302 }
303 if (start % ts) {
304 mask = (~(unsigned char) 0) << (ts - (start % ts));
305 cmask = ptr[this_unit];
306 cmask &= ~mask;
307 v <<= ts - (start % ts);
308 v |= (uint64_t) cmask;
309 start += ts - (start % ts);
310 this_unit++;
311 }
312 for (; this_unit < end_unit - 1; this_unit++) {
313 v <<= ts;
314 v |= (uint64_t) ptr[this_unit];
315 start += ts;
316 }
317 if (end % ts) {
318 cmask = ptr[this_unit];
319 cmask >>= (ts - (end % ts)) % ts;
320 v <<= (end % ts);
321 v |= (uint64_t) cmask;
322 } else {
323 v <<= ts;
324 v |= (uint64_t) ptr[this_unit];
325 }
326 break;
327 default:
328 assert(0); /* TODO: support PDP_ENDIAN */
329 }
330 return v;
331 }
332
333 static inline
334 uint64_t ctf_bitfield_unsigned_read(const uint8_t *ptr,
335 unsigned long start, unsigned long len,
336 int byte_order)
337 {
338 return (uint64_t) _ctf_bitfield_read_64(ptr, start, len, byte_order, 0);
339 }
340
341 static inline
342 int64_t ctf_bitfield_signed_read(const uint8_t *ptr,
343 unsigned long start, unsigned long len,
344 int byte_order)
345 {
346 return (int64_t) _ctf_bitfield_read_64(ptr, start, len, byte_order, 1);
347 }
348
349 #endif /* _CTF_BITFIELD_H */
This page took 0.054571 seconds and 4 git commands to generate.