Commit | Line | Data |
---|---|---|
448d3cc7 | 1 | /* |
ccd7e1c8 | 2 | * enum.c |
448d3cc7 | 3 | * |
ccd7e1c8 | 4 | * BabelTrace - Enumeration Type |
448d3cc7 | 5 | * |
c054553d | 6 | * Copyright 2010, 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
448d3cc7 | 7 | * |
ccd7e1c8 MD |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | * of this software and associated documentation files (the "Software"), to deal | |
10 | * in the Software without restriction, including without limitation the rights | |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
12 | * copies of the Software, and to permit persons to whom the Software is | |
13 | * furnished to do so, subject to the following conditions: | |
448d3cc7 | 14 | * |
ccd7e1c8 MD |
15 | * The above copyright notice and this permission notice shall be included in |
16 | * all copies or substantial portions of the Software. | |
448d3cc7 MD |
17 | */ |
18 | ||
19 | #include <babeltrace/compiler.h> | |
4c8bfb7e | 20 | #include <babeltrace/format.h> |
448d3cc7 MD |
21 | #include <stdint.h> |
22 | #include <glib.h> | |
23 | ||
c054553d MD |
24 | static |
25 | struct type_enum *_enum_type_new(struct type_class *type_class, | |
26 | struct declaration_scope *parent_scope); | |
27 | static | |
28 | void _enum_type_free(struct type *type); | |
29 | ||
d65d8abb MD |
30 | static |
31 | void enum_range_set_free(void *ptr) | |
448d3cc7 | 32 | { |
d65d8abb | 33 | g_array_unref(ptr); |
448d3cc7 MD |
34 | } |
35 | ||
d65d8abb MD |
36 | /* |
37 | * Returns a GArray or NULL. | |
38 | * Caller must release the GArray with g_array_unref(). | |
39 | */ | |
40 | GArray *enum_uint_to_quark_set(const struct type_class_enum *enum_class, | |
41 | uint64_t v) | |
448d3cc7 | 42 | { |
d65d8abb MD |
43 | struct enum_range_to_quark *iter; |
44 | GArray *qs, *ranges = NULL; | |
448d3cc7 | 45 | |
d65d8abb MD |
46 | /* Single values lookup */ |
47 | qs = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); | |
48 | ||
49 | /* Range lookup */ | |
50 | cds_list_for_each_entry(iter, &enum_class->table.range_to_quark, node) { | |
51 | if (iter->range.start._unsigned > v || iter->range.end._unsigned < v) | |
52 | continue; | |
53 | if (!ranges) { | |
54 | size_t qs_len = 0; | |
55 | ||
56 | if (qs) | |
57 | qs_len = qs->len; | |
58 | ranges = g_array_sized_new(FALSE, TRUE, | |
59 | sizeof(struct enum_range), | |
60 | qs_len + 1); | |
61 | g_array_set_size(ranges, qs_len + 1); | |
62 | if (qs) | |
63 | memcpy(ranges->data, qs->data, | |
64 | sizeof(struct enum_range) * qs_len); | |
65 | g_array_index(ranges, struct enum_range, qs_len) = iter->range; | |
66 | } else { | |
67 | g_array_set_size(ranges, ranges->len + 1); | |
68 | g_array_index(ranges, struct enum_range, ranges->len) = iter->range; | |
69 | } | |
70 | } | |
fdacfb73 | 71 | if (!ranges) { |
d65d8abb | 72 | ranges = qs; |
fdacfb73 MD |
73 | g_array_ref(ranges); |
74 | } | |
d65d8abb | 75 | return ranges; |
448d3cc7 MD |
76 | } |
77 | ||
d65d8abb MD |
78 | /* |
79 | * Returns a GArray or NULL. | |
80 | * Caller must release the GArray with g_array_unref(). | |
81 | */ | |
82 | GArray *enum_int_to_quark_set(const struct type_class_enum *enum_class, uint64_t v) | |
448d3cc7 | 83 | { |
d65d8abb MD |
84 | struct enum_range_to_quark *iter; |
85 | GArray *qs, *ranges = NULL; | |
86 | ||
87 | /* Single values lookup */ | |
88 | qs = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); | |
89 | ||
90 | /* Range lookup */ | |
91 | cds_list_for_each_entry(iter, &enum_class->table.range_to_quark, node) { | |
92 | if (iter->range.start._signed > v || iter->range.end._signed < v) | |
93 | continue; | |
94 | if (!ranges) { | |
95 | size_t qs_len = 0; | |
96 | ||
97 | if (qs) | |
98 | qs_len = qs->len; | |
99 | ranges = g_array_sized_new(FALSE, TRUE, | |
100 | sizeof(struct enum_range), | |
101 | qs_len + 1); | |
102 | g_array_set_size(ranges, qs_len + 1); | |
103 | if (qs) | |
104 | memcpy(ranges->data, qs->data, | |
105 | sizeof(struct enum_range) * qs_len); | |
106 | g_array_index(ranges, struct enum_range, qs_len) = iter->range; | |
107 | } else { | |
108 | g_array_set_size(ranges, ranges->len + 1); | |
109 | g_array_index(ranges, struct enum_range, ranges->len) = iter->range; | |
110 | } | |
111 | } | |
fdacfb73 | 112 | if (!ranges) { |
d65d8abb | 113 | ranges = qs; |
fdacfb73 MD |
114 | g_array_ref(ranges); |
115 | } | |
d65d8abb | 116 | return ranges; |
448d3cc7 MD |
117 | } |
118 | ||
d65d8abb MD |
119 | #if (__WORDSIZE == 32) |
120 | static | |
448d3cc7 MD |
121 | guint enum_val_hash(gconstpointer key) |
122 | { | |
123 | int64_t ukey = *(const int64_t *)key; | |
124 | ||
125 | return (guint)ukey ^ (guint)(ukey >> 32); | |
126 | } | |
127 | ||
d65d8abb | 128 | static |
448d3cc7 MD |
129 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) |
130 | { | |
131 | int64_t ua = *(const int64_t *)a; | |
132 | int64_t ub = *(const int64_t *)b; | |
133 | ||
134 | return ua == ub; | |
135 | } | |
136 | ||
d65d8abb | 137 | static |
448d3cc7 MD |
138 | void enum_val_free(void *ptr) |
139 | { | |
140 | g_free(ptr); | |
141 | } | |
142 | ||
d65d8abb MD |
143 | static |
144 | void enum_signed_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
145 | int64_t v, GQuark q) | |
448d3cc7 | 146 | { |
d65d8abb MD |
147 | int64_t *valuep; |
148 | GArray *array; | |
448d3cc7 | 149 | |
d65d8abb MD |
150 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); |
151 | if (!array) { | |
152 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
153 | g_array_set_size(array, 1); | |
154 | g_array_index(array, GQuark, array->len - 1) = q; | |
155 | valuep = g_new(int64_t, 1); | |
156 | *valuep = v; | |
157 | g_hash_table_insert(enum_class->table.value_to_quark_set, valuep, array); | |
158 | } else { | |
159 | g_array_set_size(array, array->len + 1); | |
160 | g_array_index(array, GQuark, array->len - 1) = q; | |
161 | } | |
448d3cc7 MD |
162 | } |
163 | ||
d65d8abb MD |
164 | static |
165 | void enum_unsigned_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
166 | uint64_t v, GQuark q) | |
448d3cc7 | 167 | { |
d65d8abb MD |
168 | uint64_t *valuep; |
169 | GArray *array; | |
448d3cc7 | 170 | |
d65d8abb MD |
171 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); |
172 | if (!array) { | |
173 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
174 | g_array_set_size(array, 1); | |
175 | g_array_index(array, GQuark, array->len - 1) = q; | |
176 | valuep = g_new(uint64_t, 1); | |
177 | *valuep = v; | |
178 | g_hash_table_insert(enum_class->table.value_to_quark_set, valuep, array); | |
179 | } else { | |
180 | g_array_set_size(array, array->len + 1); | |
181 | g_array_index(array, GQuark, array->len - 1) = q; | |
182 | } | |
448d3cc7 | 183 | } |
d65d8abb MD |
184 | #else /* __WORDSIZE != 32 */ |
185 | static | |
186 | guint enum_val_hash(gconstpointer key) | |
448d3cc7 | 187 | { |
d65d8abb | 188 | return g_direct_hash(key); |
448d3cc7 MD |
189 | } |
190 | ||
d65d8abb MD |
191 | static |
192 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) | |
448d3cc7 | 193 | { |
d65d8abb | 194 | return g_direct_equal(a, b); |
448d3cc7 MD |
195 | } |
196 | ||
d65d8abb MD |
197 | static |
198 | void enum_val_free(void *ptr) | |
448d3cc7 | 199 | { |
448d3cc7 MD |
200 | } |
201 | ||
d65d8abb MD |
202 | static |
203 | void enum_signed_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
204 | int64_t v, GQuark q) | |
205 | { | |
206 | GArray *array; | |
207 | ||
208 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, | |
209 | (gconstpointer) v); | |
210 | if (!array) { | |
211 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
212 | g_array_set_size(array, 1); | |
213 | g_array_index(array, GQuark, array->len - 1) = q; | |
214 | g_hash_table_insert(enum_class->table.value_to_quark_set, | |
380d60b1 | 215 | (gpointer) v, array); |
d65d8abb MD |
216 | } else { |
217 | g_array_set_size(array, array->len + 1); | |
218 | g_array_index(array, GQuark, array->len - 1) = q; | |
219 | } | |
220 | } | |
221 | ||
222 | static | |
223 | void enum_unsigned_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
224 | uint64_t v, GQuark q) | |
448d3cc7 | 225 | { |
d65d8abb MD |
226 | GArray *array; |
227 | ||
228 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, | |
229 | (gconstpointer) v); | |
230 | if (!array) { | |
231 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
232 | g_array_set_size(array, 1); | |
233 | g_array_index(array, GQuark, array->len - 1) = q; | |
234 | g_hash_table_insert(enum_class->table.value_to_quark_set, | |
380d60b1 | 235 | (gpointer) v, array); |
d65d8abb MD |
236 | } else { |
237 | g_array_set_size(array, array->len + 1); | |
238 | g_array_index(array, GQuark, array->len - 1) = q; | |
239 | } | |
448d3cc7 | 240 | } |
d65d8abb | 241 | #endif /* __WORDSIZE != 32 */ |
448d3cc7 | 242 | |
d65d8abb MD |
243 | GArray *enum_quark_to_range_set(const struct type_class_enum *enum_class, |
244 | GQuark q) | |
448d3cc7 | 245 | { |
47e0f2e2 MD |
246 | return g_hash_table_lookup(enum_class->table.quark_to_range_set, |
247 | (gconstpointer) (unsigned long) q); | |
448d3cc7 MD |
248 | } |
249 | ||
d65d8abb MD |
250 | static |
251 | void enum_signed_insert_range_to_quark(struct type_class_enum *enum_class, | |
252 | int64_t start, int64_t end, GQuark q) | |
448d3cc7 | 253 | { |
d65d8abb MD |
254 | struct enum_range_to_quark *rtoq; |
255 | ||
256 | rtoq = g_new(struct enum_range_to_quark, 1); | |
257 | cds_list_add(&rtoq->node, &enum_class->table.range_to_quark); | |
258 | rtoq->range.start._signed = start; | |
259 | rtoq->range.end._signed = end; | |
260 | rtoq->quark = q; | |
448d3cc7 MD |
261 | } |
262 | ||
d65d8abb MD |
263 | static |
264 | void enum_unsigned_insert_range_to_quark(struct type_class_enum *enum_class, | |
265 | uint64_t start, uint64_t end, GQuark q) | |
448d3cc7 | 266 | { |
d65d8abb MD |
267 | struct enum_range_to_quark *rtoq; |
268 | ||
269 | rtoq = g_new(struct enum_range_to_quark, 1); | |
270 | cds_list_add(&rtoq->node, &enum_class->table.range_to_quark); | |
271 | rtoq->range.start._unsigned = start; | |
272 | rtoq->range.end._unsigned = end; | |
273 | rtoq->quark = q; | |
448d3cc7 MD |
274 | } |
275 | ||
276 | void enum_signed_insert(struct type_class_enum *enum_class, | |
d65d8abb | 277 | int64_t start, int64_t end, GQuark q) |
448d3cc7 | 278 | { |
d65d8abb MD |
279 | GArray *array; |
280 | struct enum_range *range; | |
281 | ||
282 | if (start == end) { | |
283 | enum_signed_insert_value_to_quark_set(enum_class, start, q); | |
284 | } else { | |
285 | if (start > end) { | |
286 | uint64_t tmp; | |
287 | ||
288 | tmp = start; | |
289 | start = end; | |
290 | end = tmp; | |
291 | } | |
292 | enum_signed_insert_range_to_quark(enum_class, start, end, q); | |
293 | } | |
294 | ||
295 | array = g_hash_table_lookup(enum_class->table.quark_to_range_set, | |
296 | (gconstpointer) (unsigned long) q); | |
297 | if (!array) { | |
298 | array = g_array_sized_new(FALSE, TRUE, | |
299 | sizeof(struct enum_range), 1); | |
300 | g_hash_table_insert(enum_class->table.quark_to_range_set, | |
301 | (gpointer) (unsigned long) q, | |
302 | array); | |
303 | } | |
304 | g_array_set_size(array, array->len + 1); | |
305 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
306 | range->start._signed = start; | |
307 | range->end._signed = end; | |
448d3cc7 MD |
308 | } |
309 | ||
310 | void enum_unsigned_insert(struct type_class_enum *enum_class, | |
d65d8abb | 311 | uint64_t start, uint64_t end, GQuark q) |
448d3cc7 | 312 | { |
d65d8abb MD |
313 | GArray *array; |
314 | struct enum_range *range; | |
315 | ||
316 | ||
317 | if (start == end) { | |
318 | enum_unsigned_insert_value_to_quark_set(enum_class, start, q); | |
319 | } else { | |
320 | if (start > end) { | |
321 | uint64_t tmp; | |
322 | ||
323 | tmp = start; | |
324 | start = end; | |
325 | end = tmp; | |
326 | } | |
327 | enum_unsigned_insert_range_to_quark(enum_class, start, end, q); | |
328 | } | |
329 | ||
330 | array = g_hash_table_lookup(enum_class->table.quark_to_range_set, | |
331 | (gconstpointer) (unsigned long) q); | |
332 | if (!array) { | |
333 | array = g_array_sized_new(FALSE, TRUE, | |
334 | sizeof(struct enum_range), 1); | |
335 | g_hash_table_insert(enum_class->table.quark_to_range_set, | |
336 | (gpointer) (unsigned long) q, | |
337 | array); | |
338 | } | |
339 | g_array_set_size(array, array->len + 1); | |
340 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
341 | range->start._unsigned = start; | |
342 | range->end._unsigned = end; | |
448d3cc7 | 343 | } |
448d3cc7 | 344 | |
c054553d MD |
345 | size_t enum_get_nr_enumerators(struct type_class_enum *enum_class) |
346 | { | |
347 | return g_hash_table_size(enum_class->table.quark_to_range_set); | |
348 | } | |
349 | ||
4c8bfb7e MD |
350 | void enum_copy(struct stream_pos *dest, const struct format *fdest, |
351 | struct stream_pos *src, const struct format *fsrc, | |
c054553d | 352 | struct type *type) |
448d3cc7 | 353 | { |
c054553d MD |
354 | struct type_enum *_enum = container_of(type, struct type_enum, p); |
355 | struct type_class_enum *enum_class = _enum->_class; | |
47e0f2e2 | 356 | GArray *array; |
448d3cc7 MD |
357 | GQuark v; |
358 | ||
47e0f2e2 MD |
359 | array = fsrc->enum_read(src, enum_class); |
360 | assert(array); | |
c054553d MD |
361 | /* unref previous array */ |
362 | if (_enum->value) | |
363 | g_array_unref(_enum->value, TRUE); | |
364 | _enum->value = array; | |
47e0f2e2 MD |
365 | /* |
366 | * Arbitrarily choose the first one. | |
367 | * TODO: use direct underlying type read/write intead. Not doing it for | |
368 | * now to test enum read and write code. | |
369 | */ | |
370 | v = g_array_index(array, GQuark, 0); | |
448d3cc7 MD |
371 | return fdest->enum_write(dest, enum_class, v); |
372 | } | |
373 | ||
c054553d MD |
374 | static |
375 | void _enum_type_class_free(struct type_class *type_class) | |
90b676d7 | 376 | { |
c054553d MD |
377 | struct type_class_enum *enum_class = |
378 | container_of(type_class, struct enum_type_class, p); | |
d65d8abb MD |
379 | struct enum_range_to_quark *iter, *tmp; |
380 | ||
381 | g_hash_table_destroy(enum_class->table.value_to_quark_set); | |
382 | cds_list_for_each_entry_safe(iter, tmp, &enum_class->table.range_to_quark, node) { | |
383 | cds_list_del(&iter->node); | |
384 | g_free(iter); | |
385 | } | |
386 | g_hash_table_destroy(enum_class->table.quark_to_range_set); | |
90b676d7 MD |
387 | g_free(enum_class); |
388 | } | |
389 | ||
c054553d MD |
390 | struct type_class_enum * |
391 | _enum_type_class_new(const char *name, size_t len, int byte_order, | |
392 | int signedness, size_t alignment) | |
448d3cc7 | 393 | { |
4c8bfb7e | 394 | struct type_class_enum *enum_class; |
448d3cc7 MD |
395 | struct type_class_integer *int_class; |
396 | int ret; | |
397 | ||
7fe00194 | 398 | enum_class = g_new(struct type_class_enum, 1); |
d65d8abb MD |
399 | enum_class->table.value_to_quark_set = g_hash_table_new_full(enum_val_hash, |
400 | enum_val_equal, | |
401 | enum_val_free, | |
402 | enum_range_set_free); | |
403 | CDS_INIT_LIST_HEAD(&enum_class->table.range_to_quark); | |
404 | enum_class->table.quark_to_range_set = g_hash_table_new_full(g_int_hash, | |
405 | g_int_equal, | |
406 | NULL, enum_range_set_free); | |
7fe00194 | 407 | int_class = &enum_class->p; |
448d3cc7 MD |
408 | int_class->p.name = g_quark_from_string(name); |
409 | int_class->p.alignment = alignment; | |
90b676d7 | 410 | int_class->p.copy = enum_copy; |
c054553d MD |
411 | int_class->p.class_free = _enum_type_class_free; |
412 | int_class->p.type_new = _enum_type_new; | |
413 | int_class->p.type_free = _enum_type_free; | |
4c8bfb7e | 414 | int_class->p.ref = 1; |
448d3cc7 MD |
415 | int_class->len = len; |
416 | int_class->byte_order = byte_order; | |
417 | int_class->signedness = signedness; | |
448d3cc7 MD |
418 | if (int_class->p.name) { |
419 | ret = register_type(&int_class->p); | |
c054553d MD |
420 | if (ret) |
421 | goto register_error; | |
448d3cc7 MD |
422 | } |
423 | return enum_class; | |
c054553d MD |
424 | |
425 | register_error: | |
426 | { | |
427 | struct enum_range_to_quark *iter, *tmp; | |
428 | ||
429 | cds_list_for_each_entry_safe(iter, tmp, &enum_class->table.range_to_quark, node) { | |
430 | cds_list_del(&iter->node); | |
431 | g_free(iter); | |
432 | } | |
433 | } | |
434 | g_hash_table_destroy(enum_class->table.value_to_quark_set); | |
435 | g_hash_table_destroy(enum_class->table.quark_to_range_set); | |
436 | g_free(enum_class); | |
437 | return NULL; | |
438 | } | |
439 | ||
440 | static | |
441 | struct type_enum *_enum_type_new(struct type_class *type_class, | |
442 | struct declaration_scope *parent_scope) | |
443 | { | |
444 | struct type_class_enum *enum_class = | |
445 | container_of(type_class, struct type_class_enum, p); | |
446 | struct type_enum *_enum; | |
447 | ||
448 | _enum = g_new(struct type_enum, 1); | |
449 | type_class_ref(&enum_class->p); | |
450 | _enum->p._class = enum_class; | |
451 | _enum->p.ref = 1; | |
452 | _enum->value = NULL; | |
453 | return &_enum->p; | |
454 | } | |
455 | ||
456 | static | |
457 | void _enum_type_free(struct type *type) | |
458 | { | |
459 | struct type_enum *_enum = container_of(type, struct type_enum, p); | |
460 | ||
461 | type_class_unref(_enum->p._class); | |
462 | if (_enum->value) | |
463 | g_array_unref(_enum->value, TRUE); | |
464 | g_free(_enum); | |
448d3cc7 | 465 | } |