1 /*
2  * enum.c
3  *
4  * BabelTrace - Enumeration Type
5  *
6  * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
7  *
8  * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  */
28 
29 #include <babeltrace/compiler.h>
30 #include <babeltrace/format.h>
31 #include <babeltrace/types.h>
32 #include <stdint.h>
33 #include <glib.h>
34 
35 #if (__LONG_MAX__ == 2147483647L)
36 #define WORD_SIZE	32
37 #elif (__LONG_MAX__ == 9223372036854775807L)
38 #define WORD_SIZE	64
39 #else
40 #error "Unknown long size."
41 #endif
42 
43 static
44 struct bt_definition *_enum_definition_new(struct bt_declaration *declaration,
45 					struct definition_scope *parent_scope,
46 					GQuark field_name, int index,
47 					const char *root_name);
48 static
49 void _enum_definition_free(struct bt_definition *definition);
50 
51 static
enum_range_set_free(void * ptr)52 void enum_range_set_free(void *ptr)
53 {
54 	g_array_unref(ptr);
55 }
56 
57 #if (WORD_SIZE == 32)
58 static inline
get_uint_v(uint64_t * v)59 gpointer get_uint_v(uint64_t *v)
60 {
61 	return v;
62 }
63 
64 static inline
get_int_v(int64_t * v)65 gpointer get_int_v(int64_t *v)
66 {
67 	return v;
68 }
69 
70 static
enum_val_hash(gconstpointer key)71 guint enum_val_hash(gconstpointer key)
72 {
73 	int64_t ukey = *(const int64_t *)key;
74 
75 	return (guint)ukey ^ (guint)(ukey >> 32);
76 }
77 
78 static
enum_val_equal(gconstpointer a,gconstpointer b)79 gboolean enum_val_equal(gconstpointer a, gconstpointer b)
80 {
81 	int64_t ua = *(const int64_t *)a;
82 	int64_t ub = *(const int64_t *)b;
83 
84 	return ua == ub;
85 }
86 
87 static
enum_val_free(void * ptr)88 void enum_val_free(void *ptr)
89 {
90 	g_free(ptr);
91 }
92 #else  /* WORD_SIZE != 32 */
93 static inline
get_uint_v(uint64_t * v)94 gpointer get_uint_v(uint64_t *v)
95 {
96 	return (gpointer) *v;
97 }
98 
99 static inline
get_int_v(int64_t * v)100 gpointer get_int_v(int64_t *v)
101 {
102 	return (gpointer) *v;
103 }
104 
105 static
enum_val_hash(gconstpointer key)106 guint enum_val_hash(gconstpointer key)
107 {
108 	return g_direct_hash(key);
109 }
110 
111 static
enum_val_equal(gconstpointer a,gconstpointer b)112 gboolean enum_val_equal(gconstpointer a, gconstpointer b)
113 {
114 	return g_direct_equal(a, b);
115 }
116 
117 static
enum_val_free(void * ptr)118 void enum_val_free(void *ptr)
119 {
120 }
121 #endif /* WORD_SIZE != 32 */
122 
123 /*
124  * Returns a GArray or NULL.
125  * Caller must release the GArray with g_array_unref().
126  */
bt_enum_uint_to_quark_set(const struct declaration_enum * enum_declaration,uint64_t v)127 GArray *bt_enum_uint_to_quark_set(const struct declaration_enum *enum_declaration,
128 			       uint64_t v)
129 {
130 	struct enum_range_to_quark *iter;
131 	GArray *qs, *ranges = NULL;
132 
133 	/* Single values lookup */
134 	qs = g_hash_table_lookup(enum_declaration->table.value_to_quark_set,
135 				 get_uint_v(&v));
136 
137 	/* Range lookup */
138 	bt_list_for_each_entry(iter, &enum_declaration->table.range_to_quark, node) {
139 		if (iter->range.start._unsigned > v || iter->range.end._unsigned < v)
140 			continue;
141 		if (!ranges) {
142 			size_t qs_len = 0;
143 
144 			if (qs)
145 				qs_len = qs->len;
146 			ranges = g_array_sized_new(FALSE, TRUE,
147 					sizeof(GQuark),
148 					qs_len + 1);
149 			g_array_set_size(ranges, qs_len + 1);
150 			if (qs)
151 				memcpy(ranges->data, qs->data,
152 				       sizeof(GQuark) * qs_len);
153 			g_array_index(ranges, GQuark, qs_len) = iter->quark;
154 		} else {
155 			size_t qs_len = ranges->len;
156 
157 			g_array_set_size(ranges, qs_len + 1);
158 			g_array_index(ranges, GQuark, qs_len) = iter->quark;
159 		}
160 	}
161 	if (!ranges) {
162 		if (!qs)
163 			return NULL;
164 		ranges = qs;
165 		g_array_ref(ranges);
166 	}
167 	return ranges;
168 }
169 
170 /*
171  * Returns a GArray or NULL.
172  * Caller must release the GArray with g_array_unref().
173  */
bt_enum_int_to_quark_set(const struct declaration_enum * enum_declaration,int64_t v)174 GArray *bt_enum_int_to_quark_set(const struct declaration_enum *enum_declaration,
175 			      int64_t v)
176 {
177 	struct enum_range_to_quark *iter;
178 	GArray *qs, *ranges = NULL;
179 
180 	/* Single values lookup */
181 	qs = g_hash_table_lookup(enum_declaration->table.value_to_quark_set,
182 				 get_int_v(&v));
183 
184 	/* Range lookup */
185 	bt_list_for_each_entry(iter, &enum_declaration->table.range_to_quark, node) {
186 		if (iter->range.start._signed > v || iter->range.end._signed < v)
187 			continue;
188 		if (!ranges) {
189 			size_t qs_len = 0;
190 
191 			if (qs)
192 				qs_len = qs->len;
193 			ranges = g_array_sized_new(FALSE, TRUE,
194 					sizeof(GQuark),
195 					qs_len + 1);
196 			g_array_set_size(ranges, qs_len + 1);
197 			if (qs)
198 				memcpy(ranges->data, qs->data,
199 				       sizeof(GQuark) * qs_len);
200 			g_array_index(ranges, GQuark, qs_len) = iter->quark;
201 		} else {
202 			size_t qs_len = ranges->len;
203 
204 			g_array_set_size(ranges, qs_len + 1);
205 			g_array_index(ranges, GQuark, qs_len) = iter->quark;
206 		}
207 	}
208 	if (!ranges) {
209 		if (!qs)
210 			return NULL;
211 		ranges = qs;
212 		g_array_ref(ranges);
213 	}
214 	return ranges;
215 }
216 
217 static
bt_enum_unsigned_insert_value_to_quark_set(struct declaration_enum * enum_declaration,uint64_t v,GQuark q)218 void bt_enum_unsigned_insert_value_to_quark_set(struct declaration_enum *enum_declaration,
219 			 uint64_t v, GQuark q)
220 {
221 	uint64_t *valuep;
222 	GArray *array;
223 
224 	array = g_hash_table_lookup(enum_declaration->table.value_to_quark_set,
225 				    get_uint_v(&v));
226 	if (!array) {
227 		array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1);
228 		g_array_set_size(array, 1);
229 		g_array_index(array, GQuark, array->len - 1) = q;
230 #if (WORD_SIZE == 32)
231 		valuep = g_new(uint64_t, 1);
232 		*valuep = v;
233 #else  /* WORD_SIZE != 32 */
234 		valuep = get_uint_v(&v);
235 #endif /* WORD_SIZE != 32 */
236 		g_hash_table_insert(enum_declaration->table.value_to_quark_set, valuep, array);
237 	} else {
238 		g_array_set_size(array, array->len + 1);
239 		g_array_index(array, GQuark, array->len - 1) = q;
240 	}
241 }
242 
243 static
bt_enum_signed_insert_value_to_quark_set(struct declaration_enum * enum_declaration,int64_t v,GQuark q)244 void bt_enum_signed_insert_value_to_quark_set(struct declaration_enum *enum_declaration,
245 			int64_t v, GQuark q)
246 {
247 	int64_t *valuep;
248 	GArray *array;
249 
250 	array = g_hash_table_lookup(enum_declaration->table.value_to_quark_set,
251 				    get_int_v(&v));
252 	if (!array) {
253 		array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1);
254 		g_array_set_size(array, 1);
255 		g_array_index(array, GQuark, array->len - 1) = q;
256 #if (WORD_SIZE == 32)
257 		valuep = g_new(int64_t, 1);
258 		*valuep = v;
259 #else  /* WORD_SIZE != 32 */
260 		valuep = get_int_v(&v);
261 #endif /* WORD_SIZE != 32 */
262 		g_hash_table_insert(enum_declaration->table.value_to_quark_set, valuep, array);
263 	} else {
264 		g_array_set_size(array, array->len + 1);
265 		g_array_index(array, GQuark, array->len - 1) = q;
266 	}
267 }
268 
bt_enum_quark_to_range_set(const struct declaration_enum * enum_declaration,GQuark q)269 GArray *bt_enum_quark_to_range_set(const struct declaration_enum *enum_declaration,
270 				GQuark q)
271 {
272 	return g_hash_table_lookup(enum_declaration->table.quark_to_range_set,
273 				   (gconstpointer) (unsigned long) q);
274 }
275 
276 static
bt_enum_signed_insert_range_to_quark(struct declaration_enum * enum_declaration,int64_t start,int64_t end,GQuark q)277 void bt_enum_signed_insert_range_to_quark(struct declaration_enum *enum_declaration,
278                         int64_t start, int64_t end, GQuark q)
279 {
280 	struct enum_range_to_quark *rtoq;
281 
282 	rtoq = g_new(struct enum_range_to_quark, 1);
283 	bt_list_add(&rtoq->node, &enum_declaration->table.range_to_quark);
284 	rtoq->range.start._signed = start;
285 	rtoq->range.end._signed = end;
286 	rtoq->quark = q;
287 }
288 
289 static
bt_enum_unsigned_insert_range_to_quark(struct declaration_enum * enum_declaration,uint64_t start,uint64_t end,GQuark q)290 void bt_enum_unsigned_insert_range_to_quark(struct declaration_enum *enum_declaration,
291                         uint64_t start, uint64_t end, GQuark q)
292 {
293 	struct enum_range_to_quark *rtoq;
294 
295 	rtoq = g_new(struct enum_range_to_quark, 1);
296 	bt_list_add(&rtoq->node, &enum_declaration->table.range_to_quark);
297 	rtoq->range.start._unsigned = start;
298 	rtoq->range.end._unsigned = end;
299 	rtoq->quark = q;
300 }
301 
bt_enum_signed_insert(struct declaration_enum * enum_declaration,int64_t start,int64_t end,GQuark q)302 void bt_enum_signed_insert(struct declaration_enum *enum_declaration,
303                         int64_t start, int64_t end, GQuark q)
304 {
305 	GArray *array;
306 	struct enum_range *range;
307 
308 	if (start == end) {
309 		bt_enum_signed_insert_value_to_quark_set(enum_declaration, start, q);
310 	} else {
311 		if (start > end) {
312 			uint64_t tmp;
313 
314 			tmp = start;
315 			start = end;
316 			end = tmp;
317 		}
318 		bt_enum_signed_insert_range_to_quark(enum_declaration, start, end, q);
319 	}
320 
321 	array = g_hash_table_lookup(enum_declaration->table.quark_to_range_set,
322 				    (gconstpointer) (unsigned long) q);
323 	if (!array) {
324 		array = g_array_sized_new(FALSE, TRUE,
325 					  sizeof(struct enum_range), 1);
326 		g_hash_table_insert(enum_declaration->table.quark_to_range_set,
327 				    (gpointer) (unsigned long) q,
328 				    array);
329 	}
330 	g_array_set_size(array, array->len + 1);
331 	range = &g_array_index(array, struct enum_range, array->len - 1);
332 	range->start._signed = start;
333 	range->end._signed = end;
334 }
335 
bt_enum_unsigned_insert(struct declaration_enum * enum_declaration,uint64_t start,uint64_t end,GQuark q)336 void bt_enum_unsigned_insert(struct declaration_enum *enum_declaration,
337 			  uint64_t start, uint64_t end, GQuark q)
338 {
339 	GArray *array;
340 	struct enum_range *range;
341 
342 
343 	if (start == end) {
344 		bt_enum_unsigned_insert_value_to_quark_set(enum_declaration, start, q);
345 	} else {
346 		if (start > end) {
347 			uint64_t tmp;
348 
349 			tmp = start;
350 			start = end;
351 			end = tmp;
352 		}
353 		bt_enum_unsigned_insert_range_to_quark(enum_declaration, start, end, q);
354 	}
355 
356 	array = g_hash_table_lookup(enum_declaration->table.quark_to_range_set,
357 				    (gconstpointer) (unsigned long) q);
358 	if (!array) {
359 		array = g_array_sized_new(FALSE, TRUE,
360 					  sizeof(struct enum_range), 1);
361 		g_hash_table_insert(enum_declaration->table.quark_to_range_set,
362 				    (gpointer) (unsigned long) q,
363 				    array);
364 	}
365 	g_array_set_size(array, array->len + 1);
366 	range = &g_array_index(array, struct enum_range, array->len - 1);
367 	range->start._unsigned = start;
368 	range->end._unsigned = end;
369 }
370 
bt_enum_get_nr_enumerators(struct declaration_enum * enum_declaration)371 size_t bt_enum_get_nr_enumerators(struct declaration_enum *enum_declaration)
372 {
373 	return g_hash_table_size(enum_declaration->table.quark_to_range_set);
374 }
375 
376 static
_enum_declaration_free(struct bt_declaration * declaration)377 void _enum_declaration_free(struct bt_declaration *declaration)
378 {
379 	struct declaration_enum *enum_declaration =
380 		container_of(declaration, struct declaration_enum, p);
381 	struct enum_range_to_quark *iter, *tmp;
382 
383 	g_hash_table_destroy(enum_declaration->table.value_to_quark_set);
384 	bt_list_for_each_entry_safe(iter, tmp, &enum_declaration->table.range_to_quark, node) {
385 		bt_list_del(&iter->node);
386 		g_free(iter);
387 	}
388 	g_hash_table_destroy(enum_declaration->table.quark_to_range_set);
389 	bt_declaration_unref(&enum_declaration->integer_declaration->p);
390 	g_free(enum_declaration);
391 }
392 
393 struct declaration_enum *
bt_enum_declaration_new(struct declaration_integer * integer_declaration)394 	bt_enum_declaration_new(struct declaration_integer *integer_declaration)
395 {
396 	struct declaration_enum *enum_declaration;
397 
398 	enum_declaration = g_new(struct declaration_enum, 1);
399 
400 	enum_declaration->table.value_to_quark_set = g_hash_table_new_full(enum_val_hash,
401 							    enum_val_equal,
402 							    enum_val_free,
403 							    enum_range_set_free);
404 	BT_INIT_LIST_HEAD(&enum_declaration->table.range_to_quark);
405 	enum_declaration->table.quark_to_range_set = g_hash_table_new_full(g_direct_hash,
406 							g_direct_equal,
407 							NULL, enum_range_set_free);
408 	bt_declaration_ref(&integer_declaration->p);
409 	enum_declaration->integer_declaration = integer_declaration;
410 	enum_declaration->p.id = CTF_TYPE_ENUM;
411 	enum_declaration->p.alignment = integer_declaration->p.alignment;
412 	enum_declaration->p.declaration_free = _enum_declaration_free;
413 	enum_declaration->p.definition_new = _enum_definition_new;
414 	enum_declaration->p.definition_free = _enum_definition_free;
415 	enum_declaration->p.ref = 1;
416 	return enum_declaration;
417 }
418 
419 static
420 struct bt_definition *
_enum_definition_new(struct bt_declaration * declaration,struct definition_scope * parent_scope,GQuark field_name,int index,const char * root_name)421 	_enum_definition_new(struct bt_declaration *declaration,
422 			     struct definition_scope *parent_scope,
423 			     GQuark field_name, int index,
424 			     const char *root_name)
425 {
426 	struct declaration_enum *enum_declaration =
427 		container_of(declaration, struct declaration_enum, p);
428 	struct definition_enum *_enum;
429 	struct bt_definition *definition_integer_parent;
430 	int ret;
431 
432 	_enum = g_new(struct definition_enum, 1);
433 	bt_declaration_ref(&enum_declaration->p);
434 	_enum->p.declaration = declaration;
435 	_enum->declaration = enum_declaration;
436 	_enum->p.ref = 1;
437 	/*
438 	 * Use INT_MAX order to ensure that all fields of the parent
439 	 * scope are seen as being prior to this scope.
440 	 */
441 	_enum->p.index = root_name ? INT_MAX : index;
442 	_enum->p.name = field_name;
443 	_enum->p.path = bt_new_definition_path(parent_scope, field_name, root_name);
444 	_enum->p.scope = bt_new_definition_scope(parent_scope, field_name, root_name);
445 	_enum->value = NULL;
446 	ret = bt_register_field_definition(field_name, &_enum->p,
447 					parent_scope);
448 	assert(!ret);
449 	definition_integer_parent =
450 		enum_declaration->integer_declaration->p.definition_new(&enum_declaration->integer_declaration->p,
451 				_enum->p.scope,
452 				g_quark_from_static_string("container"), 0, NULL);
453 	_enum->integer = container_of(definition_integer_parent,
454 				      struct definition_integer, p);
455 	return &_enum->p;
456 }
457 
458 static
_enum_definition_free(struct bt_definition * definition)459 void _enum_definition_free(struct bt_definition *definition)
460 {
461 	struct definition_enum *_enum =
462 		container_of(definition, struct definition_enum, p);
463 
464 	bt_definition_unref(&_enum->integer->p);
465 	bt_free_definition_scope(_enum->p.scope);
466 	bt_declaration_unref(_enum->p.declaration);
467 	if (_enum->value)
468 		g_array_unref(_enum->value);
469 	g_free(_enum);
470 }
471