Fix: Incorrect variant tag validation
[babeltrace.git] / formats / ctf / writer / event-types.c
CommitLineData
273b65be
JG
1/*
2 * event-types.c
3 *
4 * Babeltrace CTF Writer
5 *
6 * Copyright 2013 EfficiOS Inc.
7 *
8 * Author: Jérémie Galarneau <jeremie.galarneau@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/ctf-writer/event-types.h>
30#include <babeltrace/ctf-writer/event-types-internal.h>
31#include <babeltrace/ctf-writer/writer-internal.h>
32#include <babeltrace/compiler.h>
33#include <babeltrace/endian.h>
34#include <float.h>
35#include <inttypes.h>
a39fa057 36#include <stdlib.h>
273b65be
JG
37
38struct range_overlap_query {
39 int64_t range_start, range_end;
40 int overlaps;
41 GQuark mapping_name;
42};
43
44static
45void bt_ctf_field_type_integer_destroy(struct bt_ctf_ref *);
46static
47void bt_ctf_field_type_enumeration_destroy(struct bt_ctf_ref *);
48static
49void bt_ctf_field_type_floating_point_destroy(struct bt_ctf_ref *);
50static
51void bt_ctf_field_type_structure_destroy(struct bt_ctf_ref *);
52static
53void bt_ctf_field_type_variant_destroy(struct bt_ctf_ref *);
54static
55void bt_ctf_field_type_array_destroy(struct bt_ctf_ref *);
56static
57void bt_ctf_field_type_sequence_destroy(struct bt_ctf_ref *);
58static
59void bt_ctf_field_type_string_destroy(struct bt_ctf_ref *);
60
61static
62void (* const type_destroy_funcs[])(struct bt_ctf_ref *) = {
63 [CTF_TYPE_INTEGER] = bt_ctf_field_type_integer_destroy,
64 [CTF_TYPE_ENUM] =
65 bt_ctf_field_type_enumeration_destroy,
66 [CTF_TYPE_FLOAT] =
67 bt_ctf_field_type_floating_point_destroy,
68 [CTF_TYPE_STRUCT] = bt_ctf_field_type_structure_destroy,
69 [CTF_TYPE_VARIANT] = bt_ctf_field_type_variant_destroy,
70 [CTF_TYPE_ARRAY] = bt_ctf_field_type_array_destroy,
71 [CTF_TYPE_SEQUENCE] = bt_ctf_field_type_sequence_destroy,
72 [CTF_TYPE_STRING] = bt_ctf_field_type_string_destroy,
73};
74
75static
76void generic_field_type_freeze(struct bt_ctf_field_type *);
77static
78void bt_ctf_field_type_enumeration_freeze(struct bt_ctf_field_type *);
79static
80void bt_ctf_field_type_structure_freeze(struct bt_ctf_field_type *);
81static
82void bt_ctf_field_type_variant_freeze(struct bt_ctf_field_type *);
83static
84void bt_ctf_field_type_array_freeze(struct bt_ctf_field_type *);
85static
86void bt_ctf_field_type_sequence_freeze(struct bt_ctf_field_type *);
87
88static
89type_freeze_func const type_freeze_funcs[] = {
90 [CTF_TYPE_INTEGER] = generic_field_type_freeze,
91 [CTF_TYPE_ENUM] = bt_ctf_field_type_enumeration_freeze,
92 [CTF_TYPE_FLOAT] = generic_field_type_freeze,
93 [CTF_TYPE_STRUCT] = bt_ctf_field_type_structure_freeze,
94 [CTF_TYPE_VARIANT] = bt_ctf_field_type_variant_freeze,
95 [CTF_TYPE_ARRAY] = bt_ctf_field_type_array_freeze,
96 [CTF_TYPE_SEQUENCE] = bt_ctf_field_type_sequence_freeze,
97 [CTF_TYPE_STRING] = generic_field_type_freeze,
98};
99
100static
101int bt_ctf_field_type_integer_serialize(struct bt_ctf_field_type *,
102 struct metadata_context *);
103static
104int bt_ctf_field_type_enumeration_serialize(struct bt_ctf_field_type *,
105 struct metadata_context *);
106static
107int bt_ctf_field_type_floating_point_serialize(
108 struct bt_ctf_field_type *, struct metadata_context *);
109static
110int bt_ctf_field_type_structure_serialize(struct bt_ctf_field_type *,
111 struct metadata_context *);
112static
113int bt_ctf_field_type_variant_serialize(struct bt_ctf_field_type *,
114 struct metadata_context *);
115static
116int bt_ctf_field_type_array_serialize(struct bt_ctf_field_type *,
117 struct metadata_context *);
118static
119int bt_ctf_field_type_sequence_serialize(struct bt_ctf_field_type *,
120 struct metadata_context *);
121static
122int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type *,
123 struct metadata_context *);
124
125static
126type_serialize_func const type_serialize_funcs[] = {
127 [CTF_TYPE_INTEGER] = bt_ctf_field_type_integer_serialize,
128 [CTF_TYPE_ENUM] =
129 bt_ctf_field_type_enumeration_serialize,
130 [CTF_TYPE_FLOAT] =
131 bt_ctf_field_type_floating_point_serialize,
132 [CTF_TYPE_STRUCT] =
133 bt_ctf_field_type_structure_serialize,
134 [CTF_TYPE_VARIANT] = bt_ctf_field_type_variant_serialize,
135 [CTF_TYPE_ARRAY] = bt_ctf_field_type_array_serialize,
136 [CTF_TYPE_SEQUENCE] = bt_ctf_field_type_sequence_serialize,
137 [CTF_TYPE_STRING] = bt_ctf_field_type_string_serialize,
138};
139
140static
141void bt_ctf_field_type_integer_set_byte_order(struct bt_ctf_field_type *,
142 int byte_order);
143static
144void bt_ctf_field_type_floating_point_set_byte_order(
145 struct bt_ctf_field_type *, int byte_order);
146
147static
148void (* const set_byte_order_funcs[])(struct bt_ctf_field_type *,
149 int) = {
150 [CTF_TYPE_INTEGER] =
151 bt_ctf_field_type_integer_set_byte_order,
152 [CTF_TYPE_FLOAT] =
153 bt_ctf_field_type_floating_point_set_byte_order,
154 [CTF_TYPE_ENUM ... CTF_TYPE_SEQUENCE] = NULL,
155};
156
157
158static
159void destroy_enumeration_mapping(struct enumeration_mapping *mapping)
160{
161 g_free(mapping);
162}
163
164static
165void destroy_structure_field(struct structure_field *field)
166{
167 if (field->type) {
168 bt_ctf_field_type_put(field->type);
169 }
170
171 g_free(field);
172}
173
174static
175void check_ranges_overlap(gpointer element, gpointer query)
176{
177 struct enumeration_mapping *mapping = element;
178 struct range_overlap_query *overlap_query = query;
179
180 if (mapping->range_start <= overlap_query->range_end
181 && overlap_query->range_start <= mapping->range_end) {
182 overlap_query->overlaps = 1;
183 overlap_query->mapping_name = mapping->string;
184 }
185
186 overlap_query->overlaps |=
187 mapping->string == overlap_query->mapping_name;
188}
189
190static
191void bt_ctf_field_type_init(struct bt_ctf_field_type *type)
192{
193 enum ctf_type_id type_id = type->declaration->id;
c4e511df 194 int ret;
273b65be
JG
195
196 assert(type && (type_id > CTF_TYPE_UNKNOWN) &&
197 (type_id < NR_CTF_TYPES));
198
199 bt_ctf_ref_init(&type->ref_count);
200 type->freeze = type_freeze_funcs[type_id];
201 type->serialize = type_serialize_funcs[type_id];
c4e511df
MD
202 ret = bt_ctf_field_type_set_byte_order(type, BT_CTF_BYTE_ORDER_NATIVE);
203 assert(!ret);
273b65be
JG
204 type->declaration->alignment = 1;
205}
206
207static
208int add_structure_field(GPtrArray *fields,
209 GHashTable *field_name_to_index,
210 struct bt_ctf_field_type *field_type,
211 const char *field_name)
212{
213 int ret = 0;
214 GQuark name_quark = g_quark_from_string(field_name);
215 struct structure_field *field;
216
217 /* Make sure structure does not contain a field of the same name */
fe0fe95c
JG
218 if (g_hash_table_lookup_extended(field_name_to_index,
219 GUINT_TO_POINTER(name_quark), NULL, NULL)) {
273b65be
JG
220 ret = -1;
221 goto end;
222 }
223
224 field = g_new0(struct structure_field, 1);
225 if (!field) {
226 ret = -1;
227 goto end;
228 }
229
230 bt_ctf_field_type_get(field_type);
231 field->name = name_quark;
232 field->type = field_type;
233 g_hash_table_insert(field_name_to_index,
234 (gpointer) (unsigned long) name_quark,
235 (gpointer) (unsigned long) fields->len);
236 g_ptr_array_add(fields, field);
237 bt_ctf_field_type_freeze(field_type);
238end:
239 return ret;
240}
241
9ce21c30
JG
242BT_HIDDEN
243int bt_ctf_field_type_validate(struct bt_ctf_field_type *type)
244{
245 int ret = 0;
246
247 if (!type) {
248 ret = -1;
249 goto end;
250 }
251
252 if (type->declaration->id == CTF_TYPE_ENUM) {
253 struct bt_ctf_field_type_enumeration *enumeration =
254 container_of(type, struct bt_ctf_field_type_enumeration,
255 parent);
256
257 ret = enumeration->entries->len ? 0 : -1;
258 }
259end:
260 return ret;
261}
262
273b65be
JG
263struct bt_ctf_field_type *bt_ctf_field_type_integer_create(unsigned int size)
264{
265 struct bt_ctf_field_type_integer *integer =
266 g_new0(struct bt_ctf_field_type_integer, 1);
267
268 if (!integer || size > 64) {
269 return NULL;
270 }
271
272 integer->parent.declaration = &integer->declaration.p;
273 integer->parent.declaration->id = CTF_TYPE_INTEGER;
274 integer->declaration.len = size;
275 integer->declaration.base = BT_CTF_INTEGER_BASE_DECIMAL;
276 integer->declaration.encoding = CTF_STRING_NONE;
277 bt_ctf_field_type_init(&integer->parent);
278 return &integer->parent;
279}
280
281int bt_ctf_field_type_integer_set_signed(struct bt_ctf_field_type *type,
282 int is_signed)
283{
284 int ret = 0;
285 struct bt_ctf_field_type_integer *integer;
286
287 if (!type || type->frozen ||
288 type->declaration->id != CTF_TYPE_INTEGER) {
289 ret = -1;
290 goto end;
291 }
292
293 integer = container_of(type, struct bt_ctf_field_type_integer, parent);
294 if (is_signed && integer->declaration.len <= 1) {
295 ret = -1;
296 goto end;
297 }
298
299 integer->declaration.signedness = !!is_signed;
300end:
301 return ret;
302}
303
304int bt_ctf_field_type_integer_set_base(struct bt_ctf_field_type *type,
305 enum bt_ctf_integer_base base)
306{
307 int ret = 0;
308
309 if (!type || type->frozen ||
310 type->declaration->id != CTF_TYPE_INTEGER) {
311 ret = -1;
312 goto end;
313 }
314
315 switch (base) {
316 case BT_CTF_INTEGER_BASE_BINARY:
317 case BT_CTF_INTEGER_BASE_OCTAL:
318 case BT_CTF_INTEGER_BASE_DECIMAL:
319 case BT_CTF_INTEGER_BASE_HEXADECIMAL:
320 {
321 struct bt_ctf_field_type_integer *integer = container_of(type,
322 struct bt_ctf_field_type_integer, parent);
323 integer->declaration.base = base;
324 break;
325 }
326 default:
327 ret = -1;
328 }
329end:
330 return ret;
331}
332
333int bt_ctf_field_type_integer_set_encoding(struct bt_ctf_field_type *type,
334 enum ctf_string_encoding encoding)
335{
336 int ret = 0;
337 struct bt_ctf_field_type_integer *integer;
338
339 if (!type || type->frozen ||
340 (type->declaration->id != CTF_TYPE_INTEGER) ||
341 (encoding < CTF_STRING_NONE) ||
342 (encoding >= CTF_STRING_UNKNOWN)) {
343 ret = -1;
344 goto end;
345 }
346
347 integer = container_of(type, struct bt_ctf_field_type_integer, parent);
348 integer->declaration.encoding = encoding;
349end:
350 return ret;
351}
352
353struct bt_ctf_field_type *bt_ctf_field_type_enumeration_create(
354 struct bt_ctf_field_type *integer_container_type)
355{
356 struct bt_ctf_field_type_enumeration *enumeration = NULL;
357
358 if (!integer_container_type) {
359 goto error;
360 }
361
362 enumeration = g_new0(struct bt_ctf_field_type_enumeration, 1);
363 if (!enumeration) {
364 goto error;
365 }
366
367 enumeration->parent.declaration = &enumeration->declaration.p;
368 enumeration->parent.declaration->id = CTF_TYPE_ENUM;
369 bt_ctf_field_type_get(integer_container_type);
370 enumeration->container = integer_container_type;
371 enumeration->entries = g_ptr_array_new_with_free_func(
372 (GDestroyNotify)destroy_enumeration_mapping);
373 bt_ctf_field_type_init(&enumeration->parent);
374 return &enumeration->parent;
375error:
376 g_free(enumeration);
377 return NULL;
378}
379
380int bt_ctf_field_type_enumeration_add_mapping(
381 struct bt_ctf_field_type *type, const char *string,
382 int64_t range_start, int64_t range_end)
383{
384 int ret = 0;
385 GQuark mapping_name;
386 struct enumeration_mapping *mapping;
387 struct bt_ctf_field_type_enumeration *enumeration;
388 struct range_overlap_query query;
a39fa057 389 char *escaped_string;
273b65be
JG
390
391 if (!type || (type->declaration->id != CTF_TYPE_ENUM) ||
392 type->frozen ||
393 (range_end < range_start)) {
394 ret = -1;
395 goto end;
396 }
397
a39fa057 398 if (!string || strlen(string) == 0) {
273b65be
JG
399 ret = -1;
400 goto end;
401 }
402
a39fa057
JG
403 escaped_string = g_strescape(string, NULL);
404 if (!escaped_string) {
405 ret = -1;
406 goto end;
407 }
408
409 mapping_name = g_quark_from_string(escaped_string);
273b65be
JG
410 query = (struct range_overlap_query) { .range_start = range_start,
411 .range_end = range_end,
412 .mapping_name = mapping_name,
413 .overlaps = 0 };
414 enumeration = container_of(type, struct bt_ctf_field_type_enumeration,
415 parent);
416
417 /* Check that the range does not overlap with one already present */
418 g_ptr_array_foreach(enumeration->entries, check_ranges_overlap, &query);
419 if (query.overlaps) {
420 ret = -1;
a39fa057 421 goto error_free;
273b65be
JG
422 }
423
424 mapping = g_new(struct enumeration_mapping, 1);
425 if (!mapping) {
426 ret = -1;
a39fa057 427 goto error_free;
273b65be
JG
428 }
429
430 *mapping = (struct enumeration_mapping) {.range_start = range_start,
431 .range_end = range_end, .string = mapping_name};
432 g_ptr_array_add(enumeration->entries, mapping);
a39fa057
JG
433error_free:
434 free(escaped_string);
273b65be
JG
435end:
436 return ret;
437}
438
439struct bt_ctf_field_type *bt_ctf_field_type_floating_point_create(void)
440{
441 struct bt_ctf_field_type_floating_point *floating_point =
442 g_new0(struct bt_ctf_field_type_floating_point, 1);
443
444 if (!floating_point) {
445 goto end;
446 }
447
448 floating_point->declaration.sign = &floating_point->sign;
449 floating_point->declaration.mantissa = &floating_point->mantissa;
450 floating_point->declaration.exp = &floating_point->exp;
451 floating_point->sign.len = 1;
452 floating_point->parent.declaration = &floating_point->declaration.p;
453 floating_point->parent.declaration->id = CTF_TYPE_FLOAT;
454 floating_point->declaration.exp->len =
455 sizeof(float) * CHAR_BIT - FLT_MANT_DIG;
456 floating_point->declaration.mantissa->len = FLT_MANT_DIG - 1;
457 floating_point->sign.p.alignment = 1;
458 floating_point->mantissa.p.alignment = 1;
459 floating_point->exp.p.alignment = 1;
460
461 bt_ctf_field_type_init(&floating_point->parent);
462end:
463 return floating_point ? &floating_point->parent : NULL;
464}
465
466int bt_ctf_field_type_floating_point_set_exponent_digits(
467 struct bt_ctf_field_type *type,
468 unsigned int exponent_digits)
469{
470 int ret = 0;
471 struct bt_ctf_field_type_floating_point *floating_point;
472
473 if (!type || type->frozen ||
474 (type->declaration->id != CTF_TYPE_FLOAT)) {
475 ret = -1;
476 goto end;
477 }
478
479 floating_point = container_of(type,
480 struct bt_ctf_field_type_floating_point, parent);
481 if ((exponent_digits != sizeof(float) * CHAR_BIT - FLT_MANT_DIG) &&
482 (exponent_digits != sizeof(double) * CHAR_BIT - DBL_MANT_DIG) &&
483 (exponent_digits !=
484 sizeof(long double) * CHAR_BIT - LDBL_MANT_DIG)) {
485 ret = -1;
486 goto end;
487 }
488
489 floating_point->declaration.exp->len = exponent_digits;
490end:
491 return ret;
492}
493
494int bt_ctf_field_type_floating_point_set_mantissa_digits(
495 struct bt_ctf_field_type *type,
496 unsigned int mantissa_digits)
497{
498 int ret = 0;
499 struct bt_ctf_field_type_floating_point *floating_point;
500
501 if (!type || type->frozen ||
502 (type->declaration->id != CTF_TYPE_FLOAT)) {
503 ret = -1;
504 goto end;
505 }
506
507 floating_point = container_of(type,
508 struct bt_ctf_field_type_floating_point, parent);
509
510 if ((mantissa_digits != FLT_MANT_DIG) &&
511 (mantissa_digits != DBL_MANT_DIG) &&
512 (mantissa_digits != LDBL_MANT_DIG)) {
513 ret = -1;
514 goto end;
515 }
516
517 floating_point->declaration.mantissa->len = mantissa_digits - 1;
518end:
519 return ret;
520}
521
522struct bt_ctf_field_type *bt_ctf_field_type_structure_create(void)
523{
524 struct bt_ctf_field_type_structure *structure =
525 g_new0(struct bt_ctf_field_type_structure, 1);
526
527 if (!structure) {
528 goto error;
529 }
530
531 structure->parent.declaration = &structure->declaration.p;
532 structure->parent.declaration->id = CTF_TYPE_STRUCT;
533 bt_ctf_field_type_init(&structure->parent);
534 structure->fields = g_ptr_array_new_with_free_func(
535 (GDestroyNotify)destroy_structure_field);
536 structure->field_name_to_index = g_hash_table_new(NULL, NULL);
537 return &structure->parent;
538error:
539 return NULL;
540}
541
542int bt_ctf_field_type_structure_add_field(struct bt_ctf_field_type *type,
543 struct bt_ctf_field_type *field_type,
544 const char *field_name)
545{
546 int ret = 0;
547 struct bt_ctf_field_type_structure *structure;
548
549 if (!type || !field_type || type->frozen ||
550 validate_identifier(field_name) ||
9ce21c30
JG
551 (type->declaration->id != CTF_TYPE_STRUCT) ||
552 bt_ctf_field_type_validate(field_type)) {
35628ac7 553 ret = -1;
273b65be
JG
554 goto end;
555 }
556
557 structure = container_of(type,
558 struct bt_ctf_field_type_structure, parent);
559 if (add_structure_field(structure->fields,
560 structure->field_name_to_index, field_type, field_name)) {
561 ret = -1;
562 goto end;
563 }
564
565 if (type->declaration->alignment < field_type->declaration->alignment) {
566 type->declaration->alignment =
567 field_type->declaration->alignment;
568 }
569end:
570 return ret;
571}
572
573struct bt_ctf_field_type *bt_ctf_field_type_variant_create(
574 struct bt_ctf_field_type *enum_tag, const char *tag_name)
575{
576 struct bt_ctf_field_type_variant *variant = NULL;
577
578 if (!enum_tag || validate_identifier(tag_name) ||
579 (enum_tag->declaration->id != CTF_TYPE_ENUM)) {
580 goto error;
581 }
582
583 variant = g_new0(struct bt_ctf_field_type_variant, 1);
584 if (!variant) {
585 goto error;
586 }
587
588 variant->parent.declaration = &variant->declaration.p;
589 variant->parent.declaration->id = CTF_TYPE_VARIANT;
590 variant->tag_name = g_string_new(tag_name);
591 bt_ctf_field_type_init(&variant->parent);
592 variant->field_name_to_index = g_hash_table_new(NULL, NULL);
593 variant->fields = g_ptr_array_new_with_free_func(
594 (GDestroyNotify)destroy_structure_field);
595 bt_ctf_field_type_get(enum_tag);
596 variant->tag = container_of(enum_tag,
597 struct bt_ctf_field_type_enumeration, parent);
598 return &variant->parent;
599error:
600 return NULL;
601}
602
603int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *type,
604 struct bt_ctf_field_type *field_type,
605 const char *field_name)
606{
607 size_t i;
608 int ret = 0;
609 int name_found = 0;
610 struct bt_ctf_field_type_variant *variant;
611 GQuark field_name_quark = g_quark_from_string(field_name);
612
613 if (!type || !field_type || type->frozen ||
614 validate_identifier(field_name) ||
9ce21c30
JG
615 (type->declaration->id != CTF_TYPE_VARIANT) ||
616 bt_ctf_field_type_validate(field_type)) {
273b65be
JG
617 ret = -1;
618 goto end;
619 }
620
621 variant = container_of(type, struct bt_ctf_field_type_variant, parent);
622 /* Make sure this name is present in the enum tag */
623 for (i = 0; i < variant->tag->entries->len; i++) {
624 struct enumeration_mapping *mapping =
625 g_ptr_array_index(variant->tag->entries, i);
626
627 if (mapping->string == field_name_quark) {
628 name_found = 1;
629 break;
630 }
631 }
632
633 if (!name_found || add_structure_field(variant->fields,
634 variant->field_name_to_index, field_type, field_name)) {
635 ret = -1;
636 goto end;
637 }
638end:
639 return ret;
640}
641
642struct bt_ctf_field_type *bt_ctf_field_type_array_create(
643 struct bt_ctf_field_type *element_type,
644 unsigned int length)
645{
646 struct bt_ctf_field_type_array *array = NULL;
647
9ce21c30
JG
648 if (!element_type || length == 0 ||
649 bt_ctf_field_type_validate(element_type)) {
273b65be
JG
650 goto error;
651 }
652
653 array = g_new0(struct bt_ctf_field_type_array, 1);
654 if (!array) {
655 goto error;
656 }
657
658 array->parent.declaration = &array->declaration.p;
659 array->parent.declaration->id = CTF_TYPE_ARRAY;
660 bt_ctf_field_type_init(&array->parent);
661 bt_ctf_field_type_get(element_type);
662 array->element_type = element_type;
663 array->length = length;
664 array->parent.declaration->alignment =
665 element_type->declaration->alignment;
666 return &array->parent;
667error:
668 return NULL;
669}
670
671struct bt_ctf_field_type *bt_ctf_field_type_sequence_create(
672 struct bt_ctf_field_type *element_type,
673 const char *length_field_name)
674{
675 struct bt_ctf_field_type_sequence *sequence = NULL;
676
9ce21c30
JG
677 if (!element_type || validate_identifier(length_field_name) ||
678 bt_ctf_field_type_validate(element_type)) {
273b65be
JG
679 goto error;
680 }
681
682 sequence = g_new0(struct bt_ctf_field_type_sequence, 1);
683 if (!sequence) {
684 goto error;
685 }
686
687 sequence->parent.declaration = &sequence->declaration.p;
688 sequence->parent.declaration->id = CTF_TYPE_SEQUENCE;
689 bt_ctf_field_type_init(&sequence->parent);
690 bt_ctf_field_type_get(element_type);
691 sequence->element_type = element_type;
692 sequence->length_field_name = g_string_new(length_field_name);
693 sequence->parent.declaration->alignment =
694 element_type->declaration->alignment;
695 return &sequence->parent;
696error:
697 return NULL;
698}
699
700struct bt_ctf_field_type *bt_ctf_field_type_string_create(void)
701{
702 struct bt_ctf_field_type_string *string =
703 g_new0(struct bt_ctf_field_type_string, 1);
704
705 if (!string) {
706 return NULL;
707 }
708
709 string->parent.declaration = &string->declaration.p;
710 string->parent.declaration->id = CTF_TYPE_STRING;
711 bt_ctf_field_type_init(&string->parent);
712 string->declaration.encoding = CTF_STRING_UTF8;
713 string->parent.declaration->alignment = CHAR_BIT;
714 return &string->parent;
715}
716
717int bt_ctf_field_type_string_set_encoding(
718 struct bt_ctf_field_type *type,
719 enum ctf_string_encoding encoding)
720{
721 int ret = 0;
722 struct bt_ctf_field_type_string *string;
723
724 if (!type || type->declaration->id != CTF_TYPE_STRING ||
725 (encoding != CTF_STRING_UTF8 &&
726 encoding != CTF_STRING_ASCII)) {
727 ret = -1;
728 goto end;
729 }
730
731 string = container_of(type, struct bt_ctf_field_type_string, parent);
732 string->declaration.encoding = encoding;
733end:
734 return ret;
735}
736
737int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *type,
738 unsigned int alignment)
739{
740 int ret = 0;
741
742 /* Alignment must be bit-aligned (1) or byte aligned */
743 if (!type || type->frozen || (alignment != 1 && (alignment & 0x7))) {
744 ret = -1;
745 goto end;
746 }
747
748 if (type->declaration->id == CTF_TYPE_STRING &&
749 alignment != CHAR_BIT) {
750 ret = -1;
751 goto end;
752 }
753
754 type->declaration->alignment = alignment;
755 ret = 0;
756end:
757 return ret;
758}
759
760int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *type,
761 enum bt_ctf_byte_order byte_order)
762{
763 int ret = 0;
764 int internal_byte_order;
765 enum ctf_type_id type_id;
766
767 if (!type || type->frozen) {
768 ret = -1;
769 goto end;
770 }
771
772 type_id = type->declaration->id;
773 switch (byte_order) {
774 case BT_CTF_BYTE_ORDER_NATIVE:
775 internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN ?
776 LITTLE_ENDIAN : BIG_ENDIAN);
777 break;
778 case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
779 internal_byte_order = LITTLE_ENDIAN;
780 break;
781 case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
782 case BT_CTF_BYTE_ORDER_NETWORK:
783 internal_byte_order = BIG_ENDIAN;
784 break;
785 default:
786 ret = -1;
787 goto end;
788 }
789
790 if (set_byte_order_funcs[type_id]) {
791 set_byte_order_funcs[type_id](type, internal_byte_order);
792 }
793end:
794 return ret;
795}
796
797void bt_ctf_field_type_get(struct bt_ctf_field_type *type)
798{
799 if (!type) {
800 return;
801 }
802
803 bt_ctf_ref_get(&type->ref_count);
804}
805
806void bt_ctf_field_type_put(struct bt_ctf_field_type *type)
807{
808 enum ctf_type_id type_id;
809
810 if (!type) {
811 return;
812 }
813
814 type_id = type->declaration->id;
815 assert(type_id > CTF_TYPE_UNKNOWN && type_id < NR_CTF_TYPES);
816 bt_ctf_ref_put(&type->ref_count, type_destroy_funcs[type_id]);
817}
818
819BT_HIDDEN
820void bt_ctf_field_type_freeze(struct bt_ctf_field_type *type)
821{
822 if (!type) {
823 return;
824 }
825
826 type->freeze(type);
827}
828
829BT_HIDDEN
830enum ctf_type_id bt_ctf_field_type_get_type_id(
831 struct bt_ctf_field_type *type)
832{
833 if (!type) {
834 return CTF_TYPE_UNKNOWN;
835 }
836
837 return type->declaration->id;
838}
839
840BT_HIDDEN
841struct bt_ctf_field_type *bt_ctf_field_type_structure_get_type(
842 struct bt_ctf_field_type_structure *structure,
843 const char *name)
844{
845 struct bt_ctf_field_type *type = NULL;
846 struct structure_field *field;
847 GQuark name_quark = g_quark_try_string(name);
848 size_t index;
849
850 if (!name_quark) {
851 goto end;
852 }
853
854 if (!g_hash_table_lookup_extended(structure->field_name_to_index,
855 GUINT_TO_POINTER(name_quark), NULL, (gpointer *)&index)) {
856 goto end;
857 }
858
859 field = structure->fields->pdata[index];
860 type = field->type;
861end:
862 return type;
863}
864
865BT_HIDDEN
866struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_type(
867 struct bt_ctf_field_type_array *array)
868{
869 assert(array);
870 return array->element_type;
871}
872
873BT_HIDDEN
874struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_type(
875 struct bt_ctf_field_type_sequence *sequence)
876{
877 assert(sequence);
878 return sequence->element_type;
879}
880
881BT_HIDDEN
882struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type(
883 struct bt_ctf_field_type_variant *variant,
884 int64_t tag_value)
885{
886 struct bt_ctf_field_type *type = NULL;
887 GQuark field_name_quark;
888 gpointer index;
889 struct structure_field *field_entry;
890 struct range_overlap_query query = {.range_start = tag_value,
891 .range_end = tag_value, .mapping_name = 0, .overlaps = 0};
892
893 g_ptr_array_foreach(variant->tag->entries, check_ranges_overlap,
894 &query);
895 if (!query.overlaps) {
896 goto end;
897 }
898
899 field_name_quark = query.mapping_name;
900 if (!g_hash_table_lookup_extended(variant->field_name_to_index,
901 GUINT_TO_POINTER(field_name_quark), NULL, &index)) {
902 goto end;
903 }
904
905 field_entry = g_ptr_array_index(variant->fields, (size_t)index);
906 type = field_entry->type;
907end:
908 return type;
909}
910
911BT_HIDDEN
912int bt_ctf_field_type_serialize(struct bt_ctf_field_type *type,
913 struct metadata_context *context)
914{
915 int ret;
916
917 if (!type || !context) {
918 ret = -1;
919 goto end;
920 }
921
922 ret = type->serialize(type, context);
923end:
924 return ret;
925}
926
927static
928void bt_ctf_field_type_integer_destroy(struct bt_ctf_ref *ref)
929{
930 struct bt_ctf_field_type_integer *integer;
931
932 if (!ref) {
933 return;
934 }
935
936 integer = container_of(
937 container_of(ref, struct bt_ctf_field_type, ref_count),
938 struct bt_ctf_field_type_integer, parent);
939 g_free(integer);
940}
941
942static
943void bt_ctf_field_type_enumeration_destroy(struct bt_ctf_ref *ref)
944{
945 struct bt_ctf_field_type_enumeration *enumeration;
946
947 if (!ref) {
948 return;
949 }
950
951 enumeration = container_of(
952 container_of(ref, struct bt_ctf_field_type, ref_count),
953 struct bt_ctf_field_type_enumeration, parent);
954 g_ptr_array_free(enumeration->entries, TRUE);
955 bt_ctf_field_type_put(enumeration->container);
956 g_free(enumeration);
957}
958
959static
960void bt_ctf_field_type_floating_point_destroy(struct bt_ctf_ref *ref)
961{
962 struct bt_ctf_field_type_floating_point *floating_point;
963
964 if (!ref) {
965 return;
966 }
967
968 floating_point = container_of(
969 container_of(ref, struct bt_ctf_field_type, ref_count),
970 struct bt_ctf_field_type_floating_point, parent);
971 g_free(floating_point);
972}
973
974static
975void bt_ctf_field_type_structure_destroy(struct bt_ctf_ref *ref)
976{
977 struct bt_ctf_field_type_structure *structure;
978
979 if (!ref) {
980 return;
981 }
982
983 structure = container_of(
984 container_of(ref, struct bt_ctf_field_type, ref_count),
985 struct bt_ctf_field_type_structure, parent);
986 g_ptr_array_free(structure->fields, TRUE);
987 g_hash_table_destroy(structure->field_name_to_index);
988 g_free(structure);
989}
990
991static
992void bt_ctf_field_type_variant_destroy(struct bt_ctf_ref *ref)
993{
994 struct bt_ctf_field_type_variant *variant;
995
996 if (!ref) {
997 return;
998 }
999
1000 variant = container_of(
1001 container_of(ref, struct bt_ctf_field_type, ref_count),
1002 struct bt_ctf_field_type_variant, parent);
1003 g_ptr_array_free(variant->fields, TRUE);
1004 g_hash_table_destroy(variant->field_name_to_index);
1005 g_string_free(variant->tag_name, TRUE);
1006 bt_ctf_field_type_put(&variant->tag->parent);
1007 g_free(variant);
1008}
1009
1010static
1011void bt_ctf_field_type_array_destroy(struct bt_ctf_ref *ref)
1012{
1013 struct bt_ctf_field_type_array *array;
1014
1015 if (!ref) {
1016 return;
1017 }
1018
1019 array = container_of(
1020 container_of(ref, struct bt_ctf_field_type, ref_count),
1021 struct bt_ctf_field_type_array, parent);
1022 bt_ctf_field_type_put(array->element_type);
1023 g_free(array);
1024}
1025
1026static
1027void bt_ctf_field_type_sequence_destroy(struct bt_ctf_ref *ref)
1028{
1029 struct bt_ctf_field_type_sequence *sequence;
1030
1031 if (!ref) {
1032 return;
1033 }
1034
1035 sequence = container_of(
1036 container_of(ref, struct bt_ctf_field_type, ref_count),
1037 struct bt_ctf_field_type_sequence, parent);
1038 bt_ctf_field_type_put(sequence->element_type);
1039 g_string_free(sequence->length_field_name, TRUE);
1040 g_free(sequence);
1041}
1042
1043static
1044void bt_ctf_field_type_string_destroy(struct bt_ctf_ref *ref)
1045{
1046 struct bt_ctf_field_type_string *string;
1047
1048 if (!ref) {
1049 return;
1050 }
1051
1052 string = container_of(
1053 container_of(ref, struct bt_ctf_field_type, ref_count),
1054 struct bt_ctf_field_type_string, parent);
1055 g_free(string);
1056}
1057
1058static
1059void generic_field_type_freeze(struct bt_ctf_field_type *type)
1060{
1061 type->frozen = 1;
1062}
1063
1064static
1065void bt_ctf_field_type_enumeration_freeze(struct bt_ctf_field_type *type)
1066{
1067 struct bt_ctf_field_type_enumeration *enumeration_type = container_of(
1068 type, struct bt_ctf_field_type_enumeration, parent);
1069
1070 generic_field_type_freeze(type);
1071 bt_ctf_field_type_freeze(enumeration_type->container);
1072}
1073
1074static
1075void freeze_structure_field(struct structure_field *field)
1076{
1077 bt_ctf_field_type_freeze(field->type);
1078}
1079
1080static
1081void bt_ctf_field_type_structure_freeze(struct bt_ctf_field_type *type)
1082{
1083 struct bt_ctf_field_type_structure *structure_type = container_of(
1084 type, struct bt_ctf_field_type_structure, parent);
1085
1086 generic_field_type_freeze(type);
1087 g_ptr_array_foreach(structure_type->fields, (GFunc)freeze_structure_field,
1088 NULL);
1089}
1090
1091static
1092void bt_ctf_field_type_variant_freeze(struct bt_ctf_field_type *type)
1093{
1094 struct bt_ctf_field_type_variant *variant_type = container_of(
1095 type, struct bt_ctf_field_type_variant, parent);
1096
1097 generic_field_type_freeze(type);
1098 g_ptr_array_foreach(variant_type->fields, (GFunc)freeze_structure_field,
1099 NULL);
1100}
1101
1102static
1103void bt_ctf_field_type_array_freeze(struct bt_ctf_field_type *type)
1104{
1105 struct bt_ctf_field_type_array *array_type = container_of(
1106 type, struct bt_ctf_field_type_array, parent);
1107
1108 generic_field_type_freeze(type);
1109 bt_ctf_field_type_freeze(array_type->element_type);
1110}
1111
1112static
1113void bt_ctf_field_type_sequence_freeze(struct bt_ctf_field_type *type)
1114{
1115 struct bt_ctf_field_type_sequence *sequence_type = container_of(
1116 type, struct bt_ctf_field_type_sequence, parent);
1117
1118 generic_field_type_freeze(type);
1119 bt_ctf_field_type_freeze(sequence_type->element_type);
1120}
1121
1122static
1123const char *get_encoding_string(enum ctf_string_encoding encoding)
1124{
1125 const char *encoding_string;
1126
1127 switch (encoding) {
1128 case CTF_STRING_NONE:
1129 encoding_string = "none";
1130 break;
1131 case CTF_STRING_ASCII:
1132 encoding_string = "ASCII";
1133 break;
1134 case CTF_STRING_UTF8:
1135 encoding_string = "UTF8";
1136 break;
1137 default:
1138 encoding_string = "unknown";
1139 break;
1140 }
1141
1142 return encoding_string;
1143}
1144
1145static
1146const char *get_integer_base_string(enum bt_ctf_integer_base base)
1147{
1148 const char *base_string;
1149
1150 switch (base) {
1151 case BT_CTF_INTEGER_BASE_DECIMAL:
1152 base_string = "decimal";
1153 break;
1154 case BT_CTF_INTEGER_BASE_HEXADECIMAL:
1155 base_string = "hexadecimal";
1156 break;
1157 case BT_CTF_INTEGER_BASE_OCTAL:
1158 base_string = "octal";
1159 break;
1160 case BT_CTF_INTEGER_BASE_BINARY:
1161 base_string = "binary";
1162 break;
1163 default:
1164 base_string = "unknown";
1165 break;
1166 }
1167
1168 return base_string;
1169}
1170
1171static
1172int bt_ctf_field_type_integer_serialize(struct bt_ctf_field_type *type,
1173 struct metadata_context *context)
1174{
1175 struct bt_ctf_field_type_integer *integer = container_of(type,
1176 struct bt_ctf_field_type_integer, parent);
1177
1178 g_string_append_printf(context->string,
1179 "integer { size = %zu; align = %zu; signed = %s; encoding = %s; base = %s; byte_order = %s; }",
1180 integer->declaration.len, type->declaration->alignment,
1181 (integer->declaration.signedness ? "true" : "false"),
1182 get_encoding_string(integer->declaration.encoding),
1183 get_integer_base_string(integer->declaration.base),
1184 get_byte_order_string(integer->declaration.byte_order));
1185 return 0;
1186}
1187
1188static
1189int bt_ctf_field_type_enumeration_serialize(struct bt_ctf_field_type *type,
1190 struct metadata_context *context)
1191{
1192 size_t entry;
9ce21c30 1193 int ret;
273b65be
JG
1194 struct bt_ctf_field_type_enumeration *enumeration = container_of(type,
1195 struct bt_ctf_field_type_enumeration, parent);
1196
9ce21c30
JG
1197 ret = bt_ctf_field_type_validate(type);
1198 if (ret) {
1199 goto end;
1200 }
1201
273b65be
JG
1202 g_string_append(context->string, "enum : ");
1203 ret = bt_ctf_field_type_serialize(enumeration->container, context);
1204 if (ret) {
1205 goto end;
1206 }
1207
1208 g_string_append(context->string, " { ");
1209 for (entry = 0; entry < enumeration->entries->len; entry++) {
1210 struct enumeration_mapping *mapping =
1211 enumeration->entries->pdata[entry];
1212
1213 if (mapping->range_start == mapping->range_end) {
a39fa057
JG
1214 g_string_append_printf(context->string,
1215 "\"%s\" = %" PRId64,
273b65be
JG
1216 g_quark_to_string(mapping->string),
1217 mapping->range_start);
1218 } else {
1219 g_string_append_printf(context->string,
a39fa057 1220 "\"%s\" = %" PRId64 " ... %" PRId64,
273b65be
JG
1221 g_quark_to_string(mapping->string),
1222 mapping->range_start, mapping->range_end);
1223 }
1224
1225 g_string_append(context->string,
1226 ((entry != (enumeration->entries->len - 1)) ?
1227 ", " : " }"));
1228 }
1229
1230 if (context->field_name->len) {
1231 g_string_append_printf(context->string, " %s",
1232 context->field_name->str);
1233 g_string_assign(context->field_name, "");
1234 }
1235end:
1236 return ret;
1237}
1238
1239static
1240int bt_ctf_field_type_floating_point_serialize(struct bt_ctf_field_type *type,
1241 struct metadata_context *context)
1242{
1243 struct bt_ctf_field_type_floating_point *floating_point = container_of(
1244 type, struct bt_ctf_field_type_floating_point, parent);
1245
1246 g_string_append_printf(context->string,
1247 "floating_point { exp_dig = %zu; mant_dig = %zu; byte_order = %s; align = %zu; }",
1248 floating_point->declaration.exp->len,
1249 floating_point->declaration.mantissa->len + 1,
1250 get_byte_order_string(floating_point->declaration.byte_order),
1251 type->declaration->alignment);
1252 return 0;
1253}
1254
1255static
1256int bt_ctf_field_type_structure_serialize(struct bt_ctf_field_type *type,
1257 struct metadata_context *context)
1258{
1259 size_t i;
1260 unsigned int indent;
1261 int ret = 0;
1262 struct bt_ctf_field_type_structure *structure = container_of(type,
1263 struct bt_ctf_field_type_structure, parent);
1264 GString *structure_field_name = context->field_name;
1265
1266 context->field_name = g_string_new("");
1267
1268 context->current_indentation_level++;
1269 g_string_append(context->string, "struct {\n");
1270
1271 for (i = 0; i < structure->fields->len; i++) {
1272 struct structure_field *field;
1273
1274 for (indent = 0; indent < context->current_indentation_level;
1275 indent++) {
1276 g_string_append_c(context->string, '\t');
1277 }
1278
1279 field = structure->fields->pdata[i];
1280 g_string_assign(context->field_name,
1281 g_quark_to_string(field->name));
1282 ret = bt_ctf_field_type_serialize(field->type, context);
1283 if (ret) {
1284 goto end;
1285 }
1286
1287 if (context->field_name->len) {
1288 g_string_append_printf(context->string, " %s",
1289 context->field_name->str);
1290 }
1291 g_string_append(context->string, ";\n");
1292 }
1293
1294 context->current_indentation_level--;
1295 for (indent = 0; indent < context->current_indentation_level;
1296 indent++) {
1297 g_string_append_c(context->string, '\t');
1298 }
1299
1300 g_string_append_printf(context->string, "} align(%zu)",
1301 type->declaration->alignment);
1302end:
1303 g_string_free(context->field_name, TRUE);
1304 context->field_name = structure_field_name;
1305 return ret;
1306}
1307
1308static
1309int bt_ctf_field_type_variant_serialize(struct bt_ctf_field_type *type,
1310 struct metadata_context *context)
1311{
1312 size_t i;
1313 unsigned int indent;
1314 int ret = 0;
1315 struct bt_ctf_field_type_variant *variant = container_of(
1316 type, struct bt_ctf_field_type_variant, parent);
1317 GString *variant_field_name = context->field_name;
1318
1319 context->field_name = g_string_new("");
1320 g_string_append_printf(context->string,
1321 "variant <%s> {\n", variant->tag_name->str);
1322 context->current_indentation_level++;
1323 for (i = 0; i < variant->fields->len; i++) {
1324 struct structure_field *field = variant->fields->pdata[i];
1325
1326 g_string_assign(context->field_name,
1327 g_quark_to_string(field->name));
1328 for (indent = 0; indent < context->current_indentation_level;
1329 indent++) {
1330 g_string_append_c(context->string, '\t');
1331 }
1332
1333 g_string_assign(context->field_name,
1334 g_quark_to_string(field->name));
1335 ret = bt_ctf_field_type_serialize(field->type, context);
1336 if (ret) {
1337 goto end;
1338 }
1339
1340 if (context->field_name->len) {
1341 g_string_append_printf(context->string, " %s;",
1342 context->field_name->str);
1343 }
1344
1345 g_string_append_c(context->string, '\n');
1346 }
1347
1348 context->current_indentation_level--;
1349 for (indent = 0; indent < context->current_indentation_level;
1350 indent++) {
1351 g_string_append_c(context->string, '\t');
1352 }
1353
1354 g_string_append(context->string, "}");
1355end:
1356 g_string_free(context->field_name, TRUE);
1357 context->field_name = variant_field_name;
1358 return ret;
1359}
1360
1361static
1362int bt_ctf_field_type_array_serialize(struct bt_ctf_field_type *type,
1363 struct metadata_context *context)
1364{
1365 int ret = 0;
1366 struct bt_ctf_field_type_array *array = container_of(type,
1367 struct bt_ctf_field_type_array, parent);
1368
1369 ret = bt_ctf_field_type_serialize(array->element_type, context);
1370 if (ret) {
1371 goto end;
1372 }
1373
1374 if (context->field_name->len) {
1375 g_string_append_printf(context->string, " %s[%u]",
1376 context->field_name->str, array->length);
1377 g_string_assign(context->field_name, "");
1378 } else {
1379 g_string_append_printf(context->string, "[%u]", array->length);
1380 }
1381end:
1382 return ret;
1383}
1384
1385static
1386int bt_ctf_field_type_sequence_serialize(struct bt_ctf_field_type *type,
1387 struct metadata_context *context)
1388{
1389 int ret = 0;
1390 struct bt_ctf_field_type_sequence *sequence = container_of(
1391 type, struct bt_ctf_field_type_sequence, parent);
1392
1393 ret = bt_ctf_field_type_serialize(sequence->element_type, context);
1394 if (ret) {
1395 goto end;
1396 }
1397
1398 if (context->field_name->len) {
1399 g_string_append_printf(context->string, " %s[%s]",
1400 context->field_name->str,
1401 sequence->length_field_name->str);
1402 g_string_assign(context->field_name, "");
1403 } else {
1404 g_string_append_printf(context->string, "[%s]",
1405 sequence->length_field_name->str);
1406 }
1407end:
1408 return ret;
1409}
1410
1411static
1412int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type *type,
1413 struct metadata_context *context)
1414{
1415 struct bt_ctf_field_type_string *string = container_of(
1416 type, struct bt_ctf_field_type_string, parent);
1417
1418 g_string_append_printf(context->string,
1419 "string { encoding = %s; }",
1420 get_encoding_string(string->declaration.encoding));
1421 return 0;
1422}
1423
1424static
1425void bt_ctf_field_type_integer_set_byte_order(struct bt_ctf_field_type *type,
1426 int byte_order)
1427{
1428 struct bt_ctf_field_type_integer *integer_type = container_of(type,
1429 struct bt_ctf_field_type_integer, parent);
1430
1431 integer_type->declaration.byte_order = byte_order;
1432}
1433
1434static
1435void bt_ctf_field_type_floating_point_set_byte_order(
1436 struct bt_ctf_field_type *type, int byte_order)
1437{
1438 struct bt_ctf_field_type_floating_point *floating_point_type =
1439 container_of(type, struct bt_ctf_field_type_floating_point,
1440 parent);
1441
1442 floating_point_type->declaration.byte_order = byte_order;
1443 floating_point_type->sign.byte_order = byte_order;
1444 floating_point_type->mantissa.byte_order = byte_order;
1445 floating_point_type->exp.byte_order = byte_order;
1446}
This page took 0.077989 seconds and 4 git commands to generate.