Commit | Line | Data |
---|---|---|
448d3cc7 | 1 | /* |
ccd7e1c8 | 2 | * enum.c |
448d3cc7 | 3 | * |
ccd7e1c8 | 4 | * BabelTrace - Enumeration Type |
448d3cc7 | 5 | * |
ccd7e1c8 | 6 | * Copyright 2010 - 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 | ||
d65d8abb MD |
24 | static |
25 | void enum_range_set_free(void *ptr) | |
448d3cc7 | 26 | { |
d65d8abb | 27 | g_array_unref(ptr); |
448d3cc7 MD |
28 | } |
29 | ||
d65d8abb MD |
30 | /* |
31 | * Returns a GArray or NULL. | |
32 | * Caller must release the GArray with g_array_unref(). | |
33 | */ | |
34 | GArray *enum_uint_to_quark_set(const struct type_class_enum *enum_class, | |
35 | uint64_t v) | |
448d3cc7 | 36 | { |
d65d8abb MD |
37 | struct enum_range_to_quark *iter; |
38 | GArray *qs, *ranges = NULL; | |
448d3cc7 | 39 | |
d65d8abb MD |
40 | /* Single values lookup */ |
41 | qs = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); | |
42 | ||
43 | /* Range lookup */ | |
44 | cds_list_for_each_entry(iter, &enum_class->table.range_to_quark, node) { | |
45 | if (iter->range.start._unsigned > v || iter->range.end._unsigned < v) | |
46 | continue; | |
47 | if (!ranges) { | |
48 | size_t qs_len = 0; | |
49 | ||
50 | if (qs) | |
51 | qs_len = qs->len; | |
52 | ranges = g_array_sized_new(FALSE, TRUE, | |
53 | sizeof(struct enum_range), | |
54 | qs_len + 1); | |
55 | g_array_set_size(ranges, qs_len + 1); | |
56 | if (qs) | |
57 | memcpy(ranges->data, qs->data, | |
58 | sizeof(struct enum_range) * qs_len); | |
59 | g_array_index(ranges, struct enum_range, qs_len) = iter->range; | |
60 | } else { | |
61 | g_array_set_size(ranges, ranges->len + 1); | |
62 | g_array_index(ranges, struct enum_range, ranges->len) = iter->range; | |
63 | } | |
64 | } | |
65 | if (!ranges) | |
66 | ranges = qs; | |
67 | return ranges; | |
448d3cc7 MD |
68 | } |
69 | ||
d65d8abb MD |
70 | /* |
71 | * Returns a GArray or NULL. | |
72 | * Caller must release the GArray with g_array_unref(). | |
73 | */ | |
74 | GArray *enum_int_to_quark_set(const struct type_class_enum *enum_class, uint64_t v) | |
448d3cc7 | 75 | { |
d65d8abb MD |
76 | struct enum_range_to_quark *iter; |
77 | GArray *qs, *ranges = NULL; | |
78 | ||
79 | /* Single values lookup */ | |
80 | qs = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); | |
81 | ||
82 | /* Range lookup */ | |
83 | cds_list_for_each_entry(iter, &enum_class->table.range_to_quark, node) { | |
84 | if (iter->range.start._signed > v || iter->range.end._signed < v) | |
85 | continue; | |
86 | if (!ranges) { | |
87 | size_t qs_len = 0; | |
88 | ||
89 | if (qs) | |
90 | qs_len = qs->len; | |
91 | ranges = g_array_sized_new(FALSE, TRUE, | |
92 | sizeof(struct enum_range), | |
93 | qs_len + 1); | |
94 | g_array_set_size(ranges, qs_len + 1); | |
95 | if (qs) | |
96 | memcpy(ranges->data, qs->data, | |
97 | sizeof(struct enum_range) * qs_len); | |
98 | g_array_index(ranges, struct enum_range, qs_len) = iter->range; | |
99 | } else { | |
100 | g_array_set_size(ranges, ranges->len + 1); | |
101 | g_array_index(ranges, struct enum_range, ranges->len) = iter->range; | |
102 | } | |
103 | } | |
104 | if (!ranges) | |
105 | ranges = qs; | |
106 | return ranges; | |
448d3cc7 MD |
107 | } |
108 | ||
d65d8abb MD |
109 | #if (__WORDSIZE == 32) |
110 | static | |
448d3cc7 MD |
111 | guint enum_val_hash(gconstpointer key) |
112 | { | |
113 | int64_t ukey = *(const int64_t *)key; | |
114 | ||
115 | return (guint)ukey ^ (guint)(ukey >> 32); | |
116 | } | |
117 | ||
d65d8abb | 118 | static |
448d3cc7 MD |
119 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) |
120 | { | |
121 | int64_t ua = *(const int64_t *)a; | |
122 | int64_t ub = *(const int64_t *)b; | |
123 | ||
124 | return ua == ub; | |
125 | } | |
126 | ||
d65d8abb | 127 | static |
448d3cc7 MD |
128 | void enum_val_free(void *ptr) |
129 | { | |
130 | g_free(ptr); | |
131 | } | |
132 | ||
d65d8abb MD |
133 | static |
134 | void enum_signed_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
135 | int64_t v, GQuark q) | |
448d3cc7 | 136 | { |
d65d8abb MD |
137 | int64_t *valuep; |
138 | GArray *array; | |
448d3cc7 | 139 | |
d65d8abb MD |
140 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); |
141 | if (!array) { | |
142 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
143 | g_array_set_size(array, 1); | |
144 | g_array_index(array, GQuark, array->len - 1) = q; | |
145 | valuep = g_new(int64_t, 1); | |
146 | *valuep = v; | |
147 | g_hash_table_insert(enum_class->table.value_to_quark_set, valuep, array); | |
148 | } else { | |
149 | g_array_set_size(array, array->len + 1); | |
150 | g_array_index(array, GQuark, array->len - 1) = q; | |
151 | } | |
448d3cc7 MD |
152 | } |
153 | ||
d65d8abb MD |
154 | static |
155 | void enum_unsigned_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
156 | uint64_t v, GQuark q) | |
448d3cc7 | 157 | { |
d65d8abb MD |
158 | uint64_t *valuep; |
159 | GArray *array; | |
448d3cc7 | 160 | |
d65d8abb MD |
161 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, &v); |
162 | if (!array) { | |
163 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
164 | g_array_set_size(array, 1); | |
165 | g_array_index(array, GQuark, array->len - 1) = q; | |
166 | valuep = g_new(uint64_t, 1); | |
167 | *valuep = v; | |
168 | g_hash_table_insert(enum_class->table.value_to_quark_set, valuep, array); | |
169 | } else { | |
170 | g_array_set_size(array, array->len + 1); | |
171 | g_array_index(array, GQuark, array->len - 1) = q; | |
172 | } | |
448d3cc7 | 173 | } |
d65d8abb MD |
174 | #else /* __WORDSIZE != 32 */ |
175 | static | |
176 | guint enum_val_hash(gconstpointer key) | |
448d3cc7 | 177 | { |
d65d8abb | 178 | return g_direct_hash(key); |
448d3cc7 MD |
179 | } |
180 | ||
d65d8abb MD |
181 | static |
182 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) | |
448d3cc7 | 183 | { |
d65d8abb | 184 | return g_direct_equal(a, b); |
448d3cc7 MD |
185 | } |
186 | ||
d65d8abb MD |
187 | static |
188 | void enum_val_free(void *ptr) | |
448d3cc7 | 189 | { |
448d3cc7 MD |
190 | } |
191 | ||
d65d8abb MD |
192 | static |
193 | void enum_signed_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
194 | int64_t v, GQuark q) | |
195 | { | |
196 | GArray *array; | |
197 | ||
198 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, | |
199 | (gconstpointer) v); | |
200 | if (!array) { | |
201 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
202 | g_array_set_size(array, 1); | |
203 | g_array_index(array, GQuark, array->len - 1) = q; | |
204 | g_hash_table_insert(enum_class->table.value_to_quark_set, | |
205 | (gconstpointer) v, array); | |
206 | } else { | |
207 | g_array_set_size(array, array->len + 1); | |
208 | g_array_index(array, GQuark, array->len - 1) = q; | |
209 | } | |
210 | } | |
211 | ||
212 | static | |
213 | void enum_unsigned_insert_value_to_quark_set(struct type_class_enum *enum_class, | |
214 | uint64_t v, GQuark q) | |
448d3cc7 | 215 | { |
d65d8abb MD |
216 | GArray *array; |
217 | ||
218 | array = g_hash_table_lookup(enum_class->table.value_to_quark_set, | |
219 | (gconstpointer) v); | |
220 | if (!array) { | |
221 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
222 | g_array_set_size(array, 1); | |
223 | g_array_index(array, GQuark, array->len - 1) = q; | |
224 | g_hash_table_insert(enum_class->table.value_to_quark_set, | |
225 | (gconstpointer) v, array); | |
226 | } else { | |
227 | g_array_set_size(array, array->len + 1); | |
228 | g_array_index(array, GQuark, array->len - 1) = q; | |
229 | } | |
448d3cc7 | 230 | } |
d65d8abb | 231 | #endif /* __WORDSIZE != 32 */ |
448d3cc7 | 232 | |
d65d8abb MD |
233 | GArray *enum_quark_to_range_set(const struct type_class_enum *enum_class, |
234 | GQuark q) | |
448d3cc7 | 235 | { |
47e0f2e2 MD |
236 | return g_hash_table_lookup(enum_class->table.quark_to_range_set, |
237 | (gconstpointer) (unsigned long) q); | |
448d3cc7 MD |
238 | } |
239 | ||
d65d8abb MD |
240 | static |
241 | void enum_signed_insert_range_to_quark(struct type_class_enum *enum_class, | |
242 | int64_t start, int64_t end, GQuark q) | |
448d3cc7 | 243 | { |
d65d8abb MD |
244 | struct enum_range_to_quark *rtoq; |
245 | ||
246 | rtoq = g_new(struct enum_range_to_quark, 1); | |
247 | cds_list_add(&rtoq->node, &enum_class->table.range_to_quark); | |
248 | rtoq->range.start._signed = start; | |
249 | rtoq->range.end._signed = end; | |
250 | rtoq->quark = q; | |
448d3cc7 MD |
251 | } |
252 | ||
d65d8abb MD |
253 | static |
254 | void enum_unsigned_insert_range_to_quark(struct type_class_enum *enum_class, | |
255 | uint64_t start, uint64_t end, GQuark q) | |
448d3cc7 | 256 | { |
d65d8abb MD |
257 | struct enum_range_to_quark *rtoq; |
258 | ||
259 | rtoq = g_new(struct enum_range_to_quark, 1); | |
260 | cds_list_add(&rtoq->node, &enum_class->table.range_to_quark); | |
261 | rtoq->range.start._unsigned = start; | |
262 | rtoq->range.end._unsigned = end; | |
263 | rtoq->quark = q; | |
448d3cc7 MD |
264 | } |
265 | ||
266 | void enum_signed_insert(struct type_class_enum *enum_class, | |
d65d8abb | 267 | int64_t start, int64_t end, GQuark q) |
448d3cc7 | 268 | { |
d65d8abb MD |
269 | GArray *array; |
270 | struct enum_range *range; | |
271 | ||
272 | if (start == end) { | |
273 | enum_signed_insert_value_to_quark_set(enum_class, start, q); | |
274 | } else { | |
275 | if (start > end) { | |
276 | uint64_t tmp; | |
277 | ||
278 | tmp = start; | |
279 | start = end; | |
280 | end = tmp; | |
281 | } | |
282 | enum_signed_insert_range_to_quark(enum_class, start, end, q); | |
283 | } | |
284 | ||
285 | array = g_hash_table_lookup(enum_class->table.quark_to_range_set, | |
286 | (gconstpointer) (unsigned long) q); | |
287 | if (!array) { | |
288 | array = g_array_sized_new(FALSE, TRUE, | |
289 | sizeof(struct enum_range), 1); | |
290 | g_hash_table_insert(enum_class->table.quark_to_range_set, | |
291 | (gpointer) (unsigned long) q, | |
292 | array); | |
293 | } | |
294 | g_array_set_size(array, array->len + 1); | |
295 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
296 | range->start._signed = start; | |
297 | range->end._signed = end; | |
448d3cc7 MD |
298 | } |
299 | ||
300 | void enum_unsigned_insert(struct type_class_enum *enum_class, | |
d65d8abb | 301 | uint64_t start, uint64_t end, GQuark q) |
448d3cc7 | 302 | { |
d65d8abb MD |
303 | GArray *array; |
304 | struct enum_range *range; | |
305 | ||
306 | ||
307 | if (start == end) { | |
308 | enum_unsigned_insert_value_to_quark_set(enum_class, start, q); | |
309 | } else { | |
310 | if (start > end) { | |
311 | uint64_t tmp; | |
312 | ||
313 | tmp = start; | |
314 | start = end; | |
315 | end = tmp; | |
316 | } | |
317 | enum_unsigned_insert_range_to_quark(enum_class, start, end, q); | |
318 | } | |
319 | ||
320 | array = g_hash_table_lookup(enum_class->table.quark_to_range_set, | |
321 | (gconstpointer) (unsigned long) q); | |
322 | if (!array) { | |
323 | array = g_array_sized_new(FALSE, TRUE, | |
324 | sizeof(struct enum_range), 1); | |
325 | g_hash_table_insert(enum_class->table.quark_to_range_set, | |
326 | (gpointer) (unsigned long) q, | |
327 | array); | |
328 | } | |
329 | g_array_set_size(array, array->len + 1); | |
330 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
331 | range->start._unsigned = start; | |
332 | range->end._unsigned = end; | |
448d3cc7 | 333 | } |
448d3cc7 | 334 | |
4c8bfb7e MD |
335 | void enum_copy(struct stream_pos *dest, const struct format *fdest, |
336 | struct stream_pos *src, const struct format *fsrc, | |
337 | const struct type_class *type_class) | |
448d3cc7 MD |
338 | { |
339 | struct type_class_enum *enum_class = | |
4c8bfb7e | 340 | container_of(type_class, struct type_class_enum, p.p); |
47e0f2e2 | 341 | GArray *array; |
448d3cc7 MD |
342 | GQuark v; |
343 | ||
47e0f2e2 MD |
344 | array = fsrc->enum_read(src, enum_class); |
345 | assert(array); | |
346 | /* | |
347 | * Arbitrarily choose the first one. | |
348 | * TODO: use direct underlying type read/write intead. Not doing it for | |
349 | * now to test enum read and write code. | |
350 | */ | |
351 | v = g_array_index(array, GQuark, 0); | |
448d3cc7 MD |
352 | return fdest->enum_write(dest, enum_class, v); |
353 | } | |
354 | ||
90b676d7 MD |
355 | void enum_type_free(struct type_class_enum *enum_class) |
356 | { | |
d65d8abb MD |
357 | struct enum_range_to_quark *iter, *tmp; |
358 | ||
359 | g_hash_table_destroy(enum_class->table.value_to_quark_set); | |
360 | cds_list_for_each_entry_safe(iter, tmp, &enum_class->table.range_to_quark, node) { | |
361 | cds_list_del(&iter->node); | |
362 | g_free(iter); | |
363 | } | |
364 | g_hash_table_destroy(enum_class->table.quark_to_range_set); | |
90b676d7 MD |
365 | g_free(enum_class); |
366 | } | |
367 | ||
368 | static | |
369 | void _enum_type_free(struct type_class *type_class) | |
370 | { | |
371 | struct type_class_enum *enum_class = | |
4c8bfb7e | 372 | container_of(type_class, struct type_class_enum, p.p); |
90b676d7 MD |
373 | enum_type_free(enum_class); |
374 | } | |
375 | ||
448d3cc7 | 376 | struct type_class_enum *enum_type_new(const char *name, |
448d3cc7 MD |
377 | size_t len, int byte_order, |
378 | int signedness, | |
379 | size_t alignment) | |
380 | { | |
4c8bfb7e | 381 | struct type_class_enum *enum_class; |
448d3cc7 MD |
382 | struct type_class_integer *int_class; |
383 | int ret; | |
384 | ||
7fe00194 | 385 | enum_class = g_new(struct type_class_enum, 1); |
d65d8abb MD |
386 | enum_class->table.value_to_quark_set = g_hash_table_new_full(enum_val_hash, |
387 | enum_val_equal, | |
388 | enum_val_free, | |
389 | enum_range_set_free); | |
390 | CDS_INIT_LIST_HEAD(&enum_class->table.range_to_quark); | |
391 | enum_class->table.quark_to_range_set = g_hash_table_new_full(g_int_hash, | |
392 | g_int_equal, | |
393 | NULL, enum_range_set_free); | |
7fe00194 | 394 | int_class = &enum_class->p; |
448d3cc7 MD |
395 | int_class->p.name = g_quark_from_string(name); |
396 | int_class->p.alignment = alignment; | |
90b676d7 MD |
397 | int_class->p.copy = enum_copy; |
398 | int_class->p.free = _enum_type_free; | |
4c8bfb7e | 399 | int_class->p.ref = 1; |
448d3cc7 MD |
400 | int_class->len = len; |
401 | int_class->byte_order = byte_order; | |
402 | int_class->signedness = signedness; | |
448d3cc7 MD |
403 | if (int_class->p.name) { |
404 | ret = register_type(&int_class->p); | |
405 | if (ret) { | |
d65d8abb MD |
406 | struct enum_range_to_quark *iter, *tmp; |
407 | ||
408 | g_hash_table_destroy(enum_class->table.value_to_quark_set); | |
409 | cds_list_for_each_entry_safe(iter, tmp, &enum_class->table.range_to_quark, node) { | |
410 | cds_list_del(&iter->node); | |
411 | g_free(iter); | |
412 | } | |
413 | g_hash_table_destroy(enum_class->table.quark_to_range_set); | |
448d3cc7 MD |
414 | g_free(enum_class); |
415 | return NULL; | |
416 | } | |
417 | } | |
418 | return enum_class; | |
419 | } |