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