Fix ctf-writer: Quote strings provided as enumeration mappings
[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)) {
273b65be
JG
553 goto end;
554 }
555
556 structure = container_of(type,
557 struct bt_ctf_field_type_structure, parent);
558 if (add_structure_field(structure->fields,
559 structure->field_name_to_index, field_type, field_name)) {
560 ret = -1;
561 goto end;
562 }
563
564 if (type->declaration->alignment < field_type->declaration->alignment) {
565 type->declaration->alignment =
566 field_type->declaration->alignment;
567 }
568end:
569 return ret;
570}
571
572struct bt_ctf_field_type *bt_ctf_field_type_variant_create(
573 struct bt_ctf_field_type *enum_tag, const char *tag_name)
574{
575 struct bt_ctf_field_type_variant *variant = NULL;
576
577 if (!enum_tag || validate_identifier(tag_name) ||
578 (enum_tag->declaration->id != CTF_TYPE_ENUM)) {
579 goto error;
580 }
581
582 variant = g_new0(struct bt_ctf_field_type_variant, 1);
583 if (!variant) {
584 goto error;
585 }
586
587 variant->parent.declaration = &variant->declaration.p;
588 variant->parent.declaration->id = CTF_TYPE_VARIANT;
589 variant->tag_name = g_string_new(tag_name);
590 bt_ctf_field_type_init(&variant->parent);
591 variant->field_name_to_index = g_hash_table_new(NULL, NULL);
592 variant->fields = g_ptr_array_new_with_free_func(
593 (GDestroyNotify)destroy_structure_field);
594 bt_ctf_field_type_get(enum_tag);
595 variant->tag = container_of(enum_tag,
596 struct bt_ctf_field_type_enumeration, parent);
597 return &variant->parent;
598error:
599 return NULL;
600}
601
602int bt_ctf_field_type_variant_add_field(struct bt_ctf_field_type *type,
603 struct bt_ctf_field_type *field_type,
604 const char *field_name)
605{
606 size_t i;
607 int ret = 0;
608 int name_found = 0;
609 struct bt_ctf_field_type_variant *variant;
610 GQuark field_name_quark = g_quark_from_string(field_name);
611
612 if (!type || !field_type || type->frozen ||
613 validate_identifier(field_name) ||
9ce21c30
JG
614 (type->declaration->id != CTF_TYPE_VARIANT) ||
615 bt_ctf_field_type_validate(field_type)) {
273b65be
JG
616 ret = -1;
617 goto end;
618 }
619
620 variant = container_of(type, struct bt_ctf_field_type_variant, parent);
621 /* Make sure this name is present in the enum tag */
622 for (i = 0; i < variant->tag->entries->len; i++) {
623 struct enumeration_mapping *mapping =
624 g_ptr_array_index(variant->tag->entries, i);
625
626 if (mapping->string == field_name_quark) {
627 name_found = 1;
628 break;
629 }
630 }
631
632 if (!name_found || add_structure_field(variant->fields,
633 variant->field_name_to_index, field_type, field_name)) {
634 ret = -1;
635 goto end;
636 }
637end:
638 return ret;
639}
640
641struct bt_ctf_field_type *bt_ctf_field_type_array_create(
642 struct bt_ctf_field_type *element_type,
643 unsigned int length)
644{
645 struct bt_ctf_field_type_array *array = NULL;
646
9ce21c30
JG
647 if (!element_type || length == 0 ||
648 bt_ctf_field_type_validate(element_type)) {
273b65be
JG
649 goto error;
650 }
651
652 array = g_new0(struct bt_ctf_field_type_array, 1);
653 if (!array) {
654 goto error;
655 }
656
657 array->parent.declaration = &array->declaration.p;
658 array->parent.declaration->id = CTF_TYPE_ARRAY;
659 bt_ctf_field_type_init(&array->parent);
660 bt_ctf_field_type_get(element_type);
661 array->element_type = element_type;
662 array->length = length;
663 array->parent.declaration->alignment =
664 element_type->declaration->alignment;
665 return &array->parent;
666error:
667 return NULL;
668}
669
670struct bt_ctf_field_type *bt_ctf_field_type_sequence_create(
671 struct bt_ctf_field_type *element_type,
672 const char *length_field_name)
673{
674 struct bt_ctf_field_type_sequence *sequence = NULL;
675
9ce21c30
JG
676 if (!element_type || validate_identifier(length_field_name) ||
677 bt_ctf_field_type_validate(element_type)) {
273b65be
JG
678 goto error;
679 }
680
681 sequence = g_new0(struct bt_ctf_field_type_sequence, 1);
682 if (!sequence) {
683 goto error;
684 }
685
686 sequence->parent.declaration = &sequence->declaration.p;
687 sequence->parent.declaration->id = CTF_TYPE_SEQUENCE;
688 bt_ctf_field_type_init(&sequence->parent);
689 bt_ctf_field_type_get(element_type);
690 sequence->element_type = element_type;
691 sequence->length_field_name = g_string_new(length_field_name);
692 sequence->parent.declaration->alignment =
693 element_type->declaration->alignment;
694 return &sequence->parent;
695error:
696 return NULL;
697}
698
699struct bt_ctf_field_type *bt_ctf_field_type_string_create(void)
700{
701 struct bt_ctf_field_type_string *string =
702 g_new0(struct bt_ctf_field_type_string, 1);
703
704 if (!string) {
705 return NULL;
706 }
707
708 string->parent.declaration = &string->declaration.p;
709 string->parent.declaration->id = CTF_TYPE_STRING;
710 bt_ctf_field_type_init(&string->parent);
711 string->declaration.encoding = CTF_STRING_UTF8;
712 string->parent.declaration->alignment = CHAR_BIT;
713 return &string->parent;
714}
715
716int bt_ctf_field_type_string_set_encoding(
717 struct bt_ctf_field_type *type,
718 enum ctf_string_encoding encoding)
719{
720 int ret = 0;
721 struct bt_ctf_field_type_string *string;
722
723 if (!type || type->declaration->id != CTF_TYPE_STRING ||
724 (encoding != CTF_STRING_UTF8 &&
725 encoding != CTF_STRING_ASCII)) {
726 ret = -1;
727 goto end;
728 }
729
730 string = container_of(type, struct bt_ctf_field_type_string, parent);
731 string->declaration.encoding = encoding;
732end:
733 return ret;
734}
735
736int bt_ctf_field_type_set_alignment(struct bt_ctf_field_type *type,
737 unsigned int alignment)
738{
739 int ret = 0;
740
741 /* Alignment must be bit-aligned (1) or byte aligned */
742 if (!type || type->frozen || (alignment != 1 && (alignment & 0x7))) {
743 ret = -1;
744 goto end;
745 }
746
747 if (type->declaration->id == CTF_TYPE_STRING &&
748 alignment != CHAR_BIT) {
749 ret = -1;
750 goto end;
751 }
752
753 type->declaration->alignment = alignment;
754 ret = 0;
755end:
756 return ret;
757}
758
759int bt_ctf_field_type_set_byte_order(struct bt_ctf_field_type *type,
760 enum bt_ctf_byte_order byte_order)
761{
762 int ret = 0;
763 int internal_byte_order;
764 enum ctf_type_id type_id;
765
766 if (!type || type->frozen) {
767 ret = -1;
768 goto end;
769 }
770
771 type_id = type->declaration->id;
772 switch (byte_order) {
773 case BT_CTF_BYTE_ORDER_NATIVE:
774 internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN ?
775 LITTLE_ENDIAN : BIG_ENDIAN);
776 break;
777 case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
778 internal_byte_order = LITTLE_ENDIAN;
779 break;
780 case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
781 case BT_CTF_BYTE_ORDER_NETWORK:
782 internal_byte_order = BIG_ENDIAN;
783 break;
784 default:
785 ret = -1;
786 goto end;
787 }
788
789 if (set_byte_order_funcs[type_id]) {
790 set_byte_order_funcs[type_id](type, internal_byte_order);
791 }
792end:
793 return ret;
794}
795
796void bt_ctf_field_type_get(struct bt_ctf_field_type *type)
797{
798 if (!type) {
799 return;
800 }
801
802 bt_ctf_ref_get(&type->ref_count);
803}
804
805void bt_ctf_field_type_put(struct bt_ctf_field_type *type)
806{
807 enum ctf_type_id type_id;
808
809 if (!type) {
810 return;
811 }
812
813 type_id = type->declaration->id;
814 assert(type_id > CTF_TYPE_UNKNOWN && type_id < NR_CTF_TYPES);
815 bt_ctf_ref_put(&type->ref_count, type_destroy_funcs[type_id]);
816}
817
818BT_HIDDEN
819void bt_ctf_field_type_freeze(struct bt_ctf_field_type *type)
820{
821 if (!type) {
822 return;
823 }
824
825 type->freeze(type);
826}
827
828BT_HIDDEN
829enum ctf_type_id bt_ctf_field_type_get_type_id(
830 struct bt_ctf_field_type *type)
831{
832 if (!type) {
833 return CTF_TYPE_UNKNOWN;
834 }
835
836 return type->declaration->id;
837}
838
839BT_HIDDEN
840struct bt_ctf_field_type *bt_ctf_field_type_structure_get_type(
841 struct bt_ctf_field_type_structure *structure,
842 const char *name)
843{
844 struct bt_ctf_field_type *type = NULL;
845 struct structure_field *field;
846 GQuark name_quark = g_quark_try_string(name);
847 size_t index;
848
849 if (!name_quark) {
850 goto end;
851 }
852
853 if (!g_hash_table_lookup_extended(structure->field_name_to_index,
854 GUINT_TO_POINTER(name_quark), NULL, (gpointer *)&index)) {
855 goto end;
856 }
857
858 field = structure->fields->pdata[index];
859 type = field->type;
860end:
861 return type;
862}
863
864BT_HIDDEN
865struct bt_ctf_field_type *bt_ctf_field_type_array_get_element_type(
866 struct bt_ctf_field_type_array *array)
867{
868 assert(array);
869 return array->element_type;
870}
871
872BT_HIDDEN
873struct bt_ctf_field_type *bt_ctf_field_type_sequence_get_element_type(
874 struct bt_ctf_field_type_sequence *sequence)
875{
876 assert(sequence);
877 return sequence->element_type;
878}
879
880BT_HIDDEN
881struct bt_ctf_field_type *bt_ctf_field_type_variant_get_field_type(
882 struct bt_ctf_field_type_variant *variant,
883 int64_t tag_value)
884{
885 struct bt_ctf_field_type *type = NULL;
886 GQuark field_name_quark;
887 gpointer index;
888 struct structure_field *field_entry;
889 struct range_overlap_query query = {.range_start = tag_value,
890 .range_end = tag_value, .mapping_name = 0, .overlaps = 0};
891
892 g_ptr_array_foreach(variant->tag->entries, check_ranges_overlap,
893 &query);
894 if (!query.overlaps) {
895 goto end;
896 }
897
898 field_name_quark = query.mapping_name;
899 if (!g_hash_table_lookup_extended(variant->field_name_to_index,
900 GUINT_TO_POINTER(field_name_quark), NULL, &index)) {
901 goto end;
902 }
903
904 field_entry = g_ptr_array_index(variant->fields, (size_t)index);
905 type = field_entry->type;
906end:
907 return type;
908}
909
910BT_HIDDEN
911int bt_ctf_field_type_serialize(struct bt_ctf_field_type *type,
912 struct metadata_context *context)
913{
914 int ret;
915
916 if (!type || !context) {
917 ret = -1;
918 goto end;
919 }
920
921 ret = type->serialize(type, context);
922end:
923 return ret;
924}
925
926static
927void bt_ctf_field_type_integer_destroy(struct bt_ctf_ref *ref)
928{
929 struct bt_ctf_field_type_integer *integer;
930
931 if (!ref) {
932 return;
933 }
934
935 integer = container_of(
936 container_of(ref, struct bt_ctf_field_type, ref_count),
937 struct bt_ctf_field_type_integer, parent);
938 g_free(integer);
939}
940
941static
942void bt_ctf_field_type_enumeration_destroy(struct bt_ctf_ref *ref)
943{
944 struct bt_ctf_field_type_enumeration *enumeration;
945
946 if (!ref) {
947 return;
948 }
949
950 enumeration = container_of(
951 container_of(ref, struct bt_ctf_field_type, ref_count),
952 struct bt_ctf_field_type_enumeration, parent);
953 g_ptr_array_free(enumeration->entries, TRUE);
954 bt_ctf_field_type_put(enumeration->container);
955 g_free(enumeration);
956}
957
958static
959void bt_ctf_field_type_floating_point_destroy(struct bt_ctf_ref *ref)
960{
961 struct bt_ctf_field_type_floating_point *floating_point;
962
963 if (!ref) {
964 return;
965 }
966
967 floating_point = container_of(
968 container_of(ref, struct bt_ctf_field_type, ref_count),
969 struct bt_ctf_field_type_floating_point, parent);
970 g_free(floating_point);
971}
972
973static
974void bt_ctf_field_type_structure_destroy(struct bt_ctf_ref *ref)
975{
976 struct bt_ctf_field_type_structure *structure;
977
978 if (!ref) {
979 return;
980 }
981
982 structure = container_of(
983 container_of(ref, struct bt_ctf_field_type, ref_count),
984 struct bt_ctf_field_type_structure, parent);
985 g_ptr_array_free(structure->fields, TRUE);
986 g_hash_table_destroy(structure->field_name_to_index);
987 g_free(structure);
988}
989
990static
991void bt_ctf_field_type_variant_destroy(struct bt_ctf_ref *ref)
992{
993 struct bt_ctf_field_type_variant *variant;
994
995 if (!ref) {
996 return;
997 }
998
999 variant = container_of(
1000 container_of(ref, struct bt_ctf_field_type, ref_count),
1001 struct bt_ctf_field_type_variant, parent);
1002 g_ptr_array_free(variant->fields, TRUE);
1003 g_hash_table_destroy(variant->field_name_to_index);
1004 g_string_free(variant->tag_name, TRUE);
1005 bt_ctf_field_type_put(&variant->tag->parent);
1006 g_free(variant);
1007}
1008
1009static
1010void bt_ctf_field_type_array_destroy(struct bt_ctf_ref *ref)
1011{
1012 struct bt_ctf_field_type_array *array;
1013
1014 if (!ref) {
1015 return;
1016 }
1017
1018 array = container_of(
1019 container_of(ref, struct bt_ctf_field_type, ref_count),
1020 struct bt_ctf_field_type_array, parent);
1021 bt_ctf_field_type_put(array->element_type);
1022 g_free(array);
1023}
1024
1025static
1026void bt_ctf_field_type_sequence_destroy(struct bt_ctf_ref *ref)
1027{
1028 struct bt_ctf_field_type_sequence *sequence;
1029
1030 if (!ref) {
1031 return;
1032 }
1033
1034 sequence = container_of(
1035 container_of(ref, struct bt_ctf_field_type, ref_count),
1036 struct bt_ctf_field_type_sequence, parent);
1037 bt_ctf_field_type_put(sequence->element_type);
1038 g_string_free(sequence->length_field_name, TRUE);
1039 g_free(sequence);
1040}
1041
1042static
1043void bt_ctf_field_type_string_destroy(struct bt_ctf_ref *ref)
1044{
1045 struct bt_ctf_field_type_string *string;
1046
1047 if (!ref) {
1048 return;
1049 }
1050
1051 string = container_of(
1052 container_of(ref, struct bt_ctf_field_type, ref_count),
1053 struct bt_ctf_field_type_string, parent);
1054 g_free(string);
1055}
1056
1057static
1058void generic_field_type_freeze(struct bt_ctf_field_type *type)
1059{
1060 type->frozen = 1;
1061}
1062
1063static
1064void bt_ctf_field_type_enumeration_freeze(struct bt_ctf_field_type *type)
1065{
1066 struct bt_ctf_field_type_enumeration *enumeration_type = container_of(
1067 type, struct bt_ctf_field_type_enumeration, parent);
1068
1069 generic_field_type_freeze(type);
1070 bt_ctf_field_type_freeze(enumeration_type->container);
1071}
1072
1073static
1074void freeze_structure_field(struct structure_field *field)
1075{
1076 bt_ctf_field_type_freeze(field->type);
1077}
1078
1079static
1080void bt_ctf_field_type_structure_freeze(struct bt_ctf_field_type *type)
1081{
1082 struct bt_ctf_field_type_structure *structure_type = container_of(
1083 type, struct bt_ctf_field_type_structure, parent);
1084
1085 generic_field_type_freeze(type);
1086 g_ptr_array_foreach(structure_type->fields, (GFunc)freeze_structure_field,
1087 NULL);
1088}
1089
1090static
1091void bt_ctf_field_type_variant_freeze(struct bt_ctf_field_type *type)
1092{
1093 struct bt_ctf_field_type_variant *variant_type = container_of(
1094 type, struct bt_ctf_field_type_variant, parent);
1095
1096 generic_field_type_freeze(type);
1097 g_ptr_array_foreach(variant_type->fields, (GFunc)freeze_structure_field,
1098 NULL);
1099}
1100
1101static
1102void bt_ctf_field_type_array_freeze(struct bt_ctf_field_type *type)
1103{
1104 struct bt_ctf_field_type_array *array_type = container_of(
1105 type, struct bt_ctf_field_type_array, parent);
1106
1107 generic_field_type_freeze(type);
1108 bt_ctf_field_type_freeze(array_type->element_type);
1109}
1110
1111static
1112void bt_ctf_field_type_sequence_freeze(struct bt_ctf_field_type *type)
1113{
1114 struct bt_ctf_field_type_sequence *sequence_type = container_of(
1115 type, struct bt_ctf_field_type_sequence, parent);
1116
1117 generic_field_type_freeze(type);
1118 bt_ctf_field_type_freeze(sequence_type->element_type);
1119}
1120
1121static
1122const char *get_encoding_string(enum ctf_string_encoding encoding)
1123{
1124 const char *encoding_string;
1125
1126 switch (encoding) {
1127 case CTF_STRING_NONE:
1128 encoding_string = "none";
1129 break;
1130 case CTF_STRING_ASCII:
1131 encoding_string = "ASCII";
1132 break;
1133 case CTF_STRING_UTF8:
1134 encoding_string = "UTF8";
1135 break;
1136 default:
1137 encoding_string = "unknown";
1138 break;
1139 }
1140
1141 return encoding_string;
1142}
1143
1144static
1145const char *get_integer_base_string(enum bt_ctf_integer_base base)
1146{
1147 const char *base_string;
1148
1149 switch (base) {
1150 case BT_CTF_INTEGER_BASE_DECIMAL:
1151 base_string = "decimal";
1152 break;
1153 case BT_CTF_INTEGER_BASE_HEXADECIMAL:
1154 base_string = "hexadecimal";
1155 break;
1156 case BT_CTF_INTEGER_BASE_OCTAL:
1157 base_string = "octal";
1158 break;
1159 case BT_CTF_INTEGER_BASE_BINARY:
1160 base_string = "binary";
1161 break;
1162 default:
1163 base_string = "unknown";
1164 break;
1165 }
1166
1167 return base_string;
1168}
1169
1170static
1171int bt_ctf_field_type_integer_serialize(struct bt_ctf_field_type *type,
1172 struct metadata_context *context)
1173{
1174 struct bt_ctf_field_type_integer *integer = container_of(type,
1175 struct bt_ctf_field_type_integer, parent);
1176
1177 g_string_append_printf(context->string,
1178 "integer { size = %zu; align = %zu; signed = %s; encoding = %s; base = %s; byte_order = %s; }",
1179 integer->declaration.len, type->declaration->alignment,
1180 (integer->declaration.signedness ? "true" : "false"),
1181 get_encoding_string(integer->declaration.encoding),
1182 get_integer_base_string(integer->declaration.base),
1183 get_byte_order_string(integer->declaration.byte_order));
1184 return 0;
1185}
1186
1187static
1188int bt_ctf_field_type_enumeration_serialize(struct bt_ctf_field_type *type,
1189 struct metadata_context *context)
1190{
1191 size_t entry;
9ce21c30 1192 int ret;
273b65be
JG
1193 struct bt_ctf_field_type_enumeration *enumeration = container_of(type,
1194 struct bt_ctf_field_type_enumeration, parent);
1195
9ce21c30
JG
1196 ret = bt_ctf_field_type_validate(type);
1197 if (ret) {
1198 goto end;
1199 }
1200
273b65be
JG
1201 g_string_append(context->string, "enum : ");
1202 ret = bt_ctf_field_type_serialize(enumeration->container, context);
1203 if (ret) {
1204 goto end;
1205 }
1206
1207 g_string_append(context->string, " { ");
1208 for (entry = 0; entry < enumeration->entries->len; entry++) {
1209 struct enumeration_mapping *mapping =
1210 enumeration->entries->pdata[entry];
1211
1212 if (mapping->range_start == mapping->range_end) {
a39fa057
JG
1213 g_string_append_printf(context->string,
1214 "\"%s\" = %" PRId64,
273b65be
JG
1215 g_quark_to_string(mapping->string),
1216 mapping->range_start);
1217 } else {
1218 g_string_append_printf(context->string,
a39fa057 1219 "\"%s\" = %" PRId64 " ... %" PRId64,
273b65be
JG
1220 g_quark_to_string(mapping->string),
1221 mapping->range_start, mapping->range_end);
1222 }
1223
1224 g_string_append(context->string,
1225 ((entry != (enumeration->entries->len - 1)) ?
1226 ", " : " }"));
1227 }
1228
1229 if (context->field_name->len) {
1230 g_string_append_printf(context->string, " %s",
1231 context->field_name->str);
1232 g_string_assign(context->field_name, "");
1233 }
1234end:
1235 return ret;
1236}
1237
1238static
1239int bt_ctf_field_type_floating_point_serialize(struct bt_ctf_field_type *type,
1240 struct metadata_context *context)
1241{
1242 struct bt_ctf_field_type_floating_point *floating_point = container_of(
1243 type, struct bt_ctf_field_type_floating_point, parent);
1244
1245 g_string_append_printf(context->string,
1246 "floating_point { exp_dig = %zu; mant_dig = %zu; byte_order = %s; align = %zu; }",
1247 floating_point->declaration.exp->len,
1248 floating_point->declaration.mantissa->len + 1,
1249 get_byte_order_string(floating_point->declaration.byte_order),
1250 type->declaration->alignment);
1251 return 0;
1252}
1253
1254static
1255int bt_ctf_field_type_structure_serialize(struct bt_ctf_field_type *type,
1256 struct metadata_context *context)
1257{
1258 size_t i;
1259 unsigned int indent;
1260 int ret = 0;
1261 struct bt_ctf_field_type_structure *structure = container_of(type,
1262 struct bt_ctf_field_type_structure, parent);
1263 GString *structure_field_name = context->field_name;
1264
1265 context->field_name = g_string_new("");
1266
1267 context->current_indentation_level++;
1268 g_string_append(context->string, "struct {\n");
1269
1270 for (i = 0; i < structure->fields->len; i++) {
1271 struct structure_field *field;
1272
1273 for (indent = 0; indent < context->current_indentation_level;
1274 indent++) {
1275 g_string_append_c(context->string, '\t');
1276 }
1277
1278 field = structure->fields->pdata[i];
1279 g_string_assign(context->field_name,
1280 g_quark_to_string(field->name));
1281 ret = bt_ctf_field_type_serialize(field->type, context);
1282 if (ret) {
1283 goto end;
1284 }
1285
1286 if (context->field_name->len) {
1287 g_string_append_printf(context->string, " %s",
1288 context->field_name->str);
1289 }
1290 g_string_append(context->string, ";\n");
1291 }
1292
1293 context->current_indentation_level--;
1294 for (indent = 0; indent < context->current_indentation_level;
1295 indent++) {
1296 g_string_append_c(context->string, '\t');
1297 }
1298
1299 g_string_append_printf(context->string, "} align(%zu)",
1300 type->declaration->alignment);
1301end:
1302 g_string_free(context->field_name, TRUE);
1303 context->field_name = structure_field_name;
1304 return ret;
1305}
1306
1307static
1308int bt_ctf_field_type_variant_serialize(struct bt_ctf_field_type *type,
1309 struct metadata_context *context)
1310{
1311 size_t i;
1312 unsigned int indent;
1313 int ret = 0;
1314 struct bt_ctf_field_type_variant *variant = container_of(
1315 type, struct bt_ctf_field_type_variant, parent);
1316 GString *variant_field_name = context->field_name;
1317
1318 context->field_name = g_string_new("");
1319 g_string_append_printf(context->string,
1320 "variant <%s> {\n", variant->tag_name->str);
1321 context->current_indentation_level++;
1322 for (i = 0; i < variant->fields->len; i++) {
1323 struct structure_field *field = variant->fields->pdata[i];
1324
1325 g_string_assign(context->field_name,
1326 g_quark_to_string(field->name));
1327 for (indent = 0; indent < context->current_indentation_level;
1328 indent++) {
1329 g_string_append_c(context->string, '\t');
1330 }
1331
1332 g_string_assign(context->field_name,
1333 g_quark_to_string(field->name));
1334 ret = bt_ctf_field_type_serialize(field->type, context);
1335 if (ret) {
1336 goto end;
1337 }
1338
1339 if (context->field_name->len) {
1340 g_string_append_printf(context->string, " %s;",
1341 context->field_name->str);
1342 }
1343
1344 g_string_append_c(context->string, '\n');
1345 }
1346
1347 context->current_indentation_level--;
1348 for (indent = 0; indent < context->current_indentation_level;
1349 indent++) {
1350 g_string_append_c(context->string, '\t');
1351 }
1352
1353 g_string_append(context->string, "}");
1354end:
1355 g_string_free(context->field_name, TRUE);
1356 context->field_name = variant_field_name;
1357 return ret;
1358}
1359
1360static
1361int bt_ctf_field_type_array_serialize(struct bt_ctf_field_type *type,
1362 struct metadata_context *context)
1363{
1364 int ret = 0;
1365 struct bt_ctf_field_type_array *array = container_of(type,
1366 struct bt_ctf_field_type_array, parent);
1367
1368 ret = bt_ctf_field_type_serialize(array->element_type, context);
1369 if (ret) {
1370 goto end;
1371 }
1372
1373 if (context->field_name->len) {
1374 g_string_append_printf(context->string, " %s[%u]",
1375 context->field_name->str, array->length);
1376 g_string_assign(context->field_name, "");
1377 } else {
1378 g_string_append_printf(context->string, "[%u]", array->length);
1379 }
1380end:
1381 return ret;
1382}
1383
1384static
1385int bt_ctf_field_type_sequence_serialize(struct bt_ctf_field_type *type,
1386 struct metadata_context *context)
1387{
1388 int ret = 0;
1389 struct bt_ctf_field_type_sequence *sequence = container_of(
1390 type, struct bt_ctf_field_type_sequence, parent);
1391
1392 ret = bt_ctf_field_type_serialize(sequence->element_type, context);
1393 if (ret) {
1394 goto end;
1395 }
1396
1397 if (context->field_name->len) {
1398 g_string_append_printf(context->string, " %s[%s]",
1399 context->field_name->str,
1400 sequence->length_field_name->str);
1401 g_string_assign(context->field_name, "");
1402 } else {
1403 g_string_append_printf(context->string, "[%s]",
1404 sequence->length_field_name->str);
1405 }
1406end:
1407 return ret;
1408}
1409
1410static
1411int bt_ctf_field_type_string_serialize(struct bt_ctf_field_type *type,
1412 struct metadata_context *context)
1413{
1414 struct bt_ctf_field_type_string *string = container_of(
1415 type, struct bt_ctf_field_type_string, parent);
1416
1417 g_string_append_printf(context->string,
1418 "string { encoding = %s; }",
1419 get_encoding_string(string->declaration.encoding));
1420 return 0;
1421}
1422
1423static
1424void bt_ctf_field_type_integer_set_byte_order(struct bt_ctf_field_type *type,
1425 int byte_order)
1426{
1427 struct bt_ctf_field_type_integer *integer_type = container_of(type,
1428 struct bt_ctf_field_type_integer, parent);
1429
1430 integer_type->declaration.byte_order = byte_order;
1431}
1432
1433static
1434void bt_ctf_field_type_floating_point_set_byte_order(
1435 struct bt_ctf_field_type *type, int byte_order)
1436{
1437 struct bt_ctf_field_type_floating_point *floating_point_type =
1438 container_of(type, struct bt_ctf_field_type_floating_point,
1439 parent);
1440
1441 floating_point_type->declaration.byte_order = byte_order;
1442 floating_point_type->sign.byte_order = byte_order;
1443 floating_point_type->mantissa.byte_order = byte_order;
1444 floating_point_type->exp.byte_order = byte_order;
1445}
This page took 0.074488 seconds and 4 git commands to generate.