Tests: Test CTF-IR event-field's getters
[babeltrace.git] / formats / ctf / ir / event-fields.c
CommitLineData
273b65be
JG
1/*
2 * event-fields.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-fields.h>
adc315b8
JG
30#include <babeltrace/ctf-ir/event-fields-internal.h>
31#include <babeltrace/ctf-ir/event-types-internal.h>
273b65be
JG
32#include <babeltrace/compiler.h>
33
34#define PACKET_LEN_INCREMENT (getpagesize() * 8 * CHAR_BIT)
35
36static
37struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *);
38static
39struct bt_ctf_field *bt_ctf_field_enumeration_create(
40 struct bt_ctf_field_type *);
41static
42struct bt_ctf_field *bt_ctf_field_floating_point_create(
43 struct bt_ctf_field_type *);
44static
45struct bt_ctf_field *bt_ctf_field_structure_create(
46 struct bt_ctf_field_type *);
47static
48struct bt_ctf_field *bt_ctf_field_variant_create(
49 struct bt_ctf_field_type *);
50static
51struct bt_ctf_field *bt_ctf_field_array_create(
52 struct bt_ctf_field_type *);
53static
54struct bt_ctf_field *bt_ctf_field_sequence_create(
55 struct bt_ctf_field_type *);
56static
57struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *);
58
59static
60void bt_ctf_field_destroy(struct bt_ctf_ref *);
61static
62void bt_ctf_field_integer_destroy(struct bt_ctf_field *);
63static
64void bt_ctf_field_enumeration_destroy(struct bt_ctf_field *);
65static
66void bt_ctf_field_floating_point_destroy(struct bt_ctf_field *);
67static
68void bt_ctf_field_structure_destroy(struct bt_ctf_field *);
69static
70void bt_ctf_field_variant_destroy(struct bt_ctf_field *);
71static
72void bt_ctf_field_array_destroy(struct bt_ctf_field *);
73static
74void bt_ctf_field_sequence_destroy(struct bt_ctf_field *);
75static
76void bt_ctf_field_string_destroy(struct bt_ctf_field *);
77
78static
79int bt_ctf_field_generic_validate(struct bt_ctf_field *field);
80static
81int bt_ctf_field_structure_validate(struct bt_ctf_field *field);
82static
83int bt_ctf_field_variant_validate(struct bt_ctf_field *field);
84static
85int bt_ctf_field_enumeration_validate(struct bt_ctf_field *field);
86static
87int bt_ctf_field_array_validate(struct bt_ctf_field *field);
88static
89int bt_ctf_field_sequence_validate(struct bt_ctf_field *field);
90
91static
92int bt_ctf_field_integer_serialize(struct bt_ctf_field *,
93 struct ctf_stream_pos *);
94static
95int bt_ctf_field_enumeration_serialize(struct bt_ctf_field *,
96 struct ctf_stream_pos *);
97static
98int bt_ctf_field_floating_point_serialize(struct bt_ctf_field *,
99 struct ctf_stream_pos *);
100static
101int bt_ctf_field_structure_serialize(struct bt_ctf_field *,
102 struct ctf_stream_pos *);
103static
104int bt_ctf_field_variant_serialize(struct bt_ctf_field *,
105 struct ctf_stream_pos *);
106static
107int bt_ctf_field_array_serialize(struct bt_ctf_field *,
108 struct ctf_stream_pos *);
109static
110int bt_ctf_field_sequence_serialize(struct bt_ctf_field *,
111 struct ctf_stream_pos *);
112static
113int bt_ctf_field_string_serialize(struct bt_ctf_field *,
114 struct ctf_stream_pos *);
115
116static
117int increase_packet_size(struct ctf_stream_pos *pos);
118
119static
120struct bt_ctf_field *(*field_create_funcs[])(
121 struct bt_ctf_field_type *) = {
122 [CTF_TYPE_INTEGER] = bt_ctf_field_integer_create,
123 [CTF_TYPE_ENUM] = bt_ctf_field_enumeration_create,
124 [CTF_TYPE_FLOAT] =
125 bt_ctf_field_floating_point_create,
126 [CTF_TYPE_STRUCT] = bt_ctf_field_structure_create,
127 [CTF_TYPE_VARIANT] = bt_ctf_field_variant_create,
128 [CTF_TYPE_ARRAY] = bt_ctf_field_array_create,
129 [CTF_TYPE_SEQUENCE] = bt_ctf_field_sequence_create,
130 [CTF_TYPE_STRING] = bt_ctf_field_string_create,
131};
132
133static
134void (*field_destroy_funcs[])(struct bt_ctf_field *) = {
135 [CTF_TYPE_INTEGER] = bt_ctf_field_integer_destroy,
136 [CTF_TYPE_ENUM] = bt_ctf_field_enumeration_destroy,
137 [CTF_TYPE_FLOAT] =
138 bt_ctf_field_floating_point_destroy,
139 [CTF_TYPE_STRUCT] = bt_ctf_field_structure_destroy,
140 [CTF_TYPE_VARIANT] = bt_ctf_field_variant_destroy,
141 [CTF_TYPE_ARRAY] = bt_ctf_field_array_destroy,
142 [CTF_TYPE_SEQUENCE] = bt_ctf_field_sequence_destroy,
143 [CTF_TYPE_STRING] = bt_ctf_field_string_destroy,
144};
145
146static
147int (*field_validate_funcs[])(struct bt_ctf_field *) = {
148 [CTF_TYPE_INTEGER] = bt_ctf_field_generic_validate,
149 [CTF_TYPE_ENUM] = bt_ctf_field_enumeration_validate,
150 [CTF_TYPE_FLOAT] = bt_ctf_field_generic_validate,
151 [CTF_TYPE_STRUCT] = bt_ctf_field_structure_validate,
152 [CTF_TYPE_VARIANT] = bt_ctf_field_variant_validate,
153 [CTF_TYPE_ARRAY] = bt_ctf_field_array_validate,
154 [CTF_TYPE_SEQUENCE] = bt_ctf_field_sequence_validate,
155 [CTF_TYPE_STRING] = bt_ctf_field_generic_validate,
156};
157
158static
159int (*field_serialize_funcs[])(struct bt_ctf_field *,
160 struct ctf_stream_pos *) = {
161 [CTF_TYPE_INTEGER] = bt_ctf_field_integer_serialize,
162 [CTF_TYPE_ENUM] = bt_ctf_field_enumeration_serialize,
163 [CTF_TYPE_FLOAT] =
164 bt_ctf_field_floating_point_serialize,
165 [CTF_TYPE_STRUCT] = bt_ctf_field_structure_serialize,
166 [CTF_TYPE_VARIANT] = bt_ctf_field_variant_serialize,
167 [CTF_TYPE_ARRAY] = bt_ctf_field_array_serialize,
168 [CTF_TYPE_SEQUENCE] = bt_ctf_field_sequence_serialize,
169 [CTF_TYPE_STRING] = bt_ctf_field_string_serialize,
170};
171
172struct bt_ctf_field *bt_ctf_field_create(struct bt_ctf_field_type *type)
173{
174 struct bt_ctf_field *field = NULL;
175 enum ctf_type_id type_id;
176
177 if (!type) {
178 goto error;
179 }
180
181 type_id = bt_ctf_field_type_get_type_id(type);
9ce21c30
JG
182 if (type_id <= CTF_TYPE_UNKNOWN || type_id >= NR_CTF_TYPES ||
183 bt_ctf_field_type_validate(type)) {
273b65be
JG
184 goto error;
185 }
186
187 field = field_create_funcs[type_id](type);
188 if (!field) {
189 goto error;
190 }
191
192 /* The type's declaration can't change after this point */
193 bt_ctf_field_type_freeze(type);
194 bt_ctf_field_type_get(type);
195 bt_ctf_ref_init(&field->ref_count);
196 field->type = type;
197error:
198 return field;
199}
200
201void bt_ctf_field_get(struct bt_ctf_field *field)
202{
203 if (field) {
204 bt_ctf_ref_get(&field->ref_count);
205 }
206}
207
208void bt_ctf_field_put(struct bt_ctf_field *field)
209{
210 if (field) {
211 bt_ctf_ref_put(&field->ref_count, bt_ctf_field_destroy);
212 }
213}
214
cd95e351
JG
215struct bt_ctf_field_type *bt_ctf_field_get_type(struct bt_ctf_field *field)
216{
217 struct bt_ctf_field_type *ret = NULL;
218
219 if (!field) {
220 goto end;
221 }
222
223 ret = field->type;
224 bt_ctf_field_type_get(ret);
225end:
226 return ret;
227}
228
229struct bt_ctf_field *bt_ctf_field_sequence_get_length(
230 struct bt_ctf_field *field)
231{
232 struct bt_ctf_field *ret = NULL;
233 struct bt_ctf_field_sequence *sequence;
234
235 if (!field) {
236 goto end;
237 }
238
239 if (bt_ctf_field_type_get_type_id(field->type) !=
240 CTF_TYPE_SEQUENCE) {
241 goto end;
242 }
243
244 sequence = container_of(field, struct bt_ctf_field_sequence, parent);
245 ret = sequence->length;
246 bt_ctf_field_get(ret);
247end:
248 return ret;
249}
250
273b65be
JG
251int bt_ctf_field_sequence_set_length(struct bt_ctf_field *field,
252 struct bt_ctf_field *length_field)
253{
254 int ret = 0;
255 struct bt_ctf_field_type_integer *length_type;
256 struct bt_ctf_field_integer *length;
257 struct bt_ctf_field_sequence *sequence;
258 uint64_t sequence_length;
259
260 if (!field || !length_field) {
261 ret = -1;
262 goto end;
263 }
264 if (bt_ctf_field_type_get_type_id(length_field->type) !=
265 CTF_TYPE_INTEGER) {
266 ret = -1;
267 goto end;
268 }
269
270 length_type = container_of(length_field->type,
271 struct bt_ctf_field_type_integer, parent);
152e7331 272 /* The length field must be unsigned */
273b65be
JG
273 if (length_type->declaration.signedness) {
274 ret = -1;
275 goto end;
276 }
277
278 length = container_of(length_field, struct bt_ctf_field_integer,
279 parent);
280 sequence_length = length->definition.value._unsigned;
281 sequence = container_of(field, struct bt_ctf_field_sequence, parent);
282 if (sequence->elements) {
283 g_ptr_array_free(sequence->elements, TRUE);
284 bt_ctf_field_put(sequence->length);
285 }
286
fe0fe95c 287 sequence->elements = g_ptr_array_sized_new((size_t)sequence_length);
273b65be
JG
288 if (!sequence->elements) {
289 ret = -1;
290 goto end;
291 }
292
fe0fe95c
JG
293 g_ptr_array_set_free_func(sequence->elements,
294 (GDestroyNotify)bt_ctf_field_put);
273b65be
JG
295 g_ptr_array_set_size(sequence->elements, (size_t)sequence_length);
296 bt_ctf_field_get(length_field);
297 sequence->length = length_field;
298end:
299 return ret;
300}
301
302struct bt_ctf_field *bt_ctf_field_structure_get_field(
303 struct bt_ctf_field *field, const char *name)
304{
305 struct bt_ctf_field *new_field = NULL;
306 GQuark field_quark;
307 struct bt_ctf_field_structure *structure;
308 struct bt_ctf_field_type_structure *structure_type;
309 struct bt_ctf_field_type *field_type;
310 size_t index;
311
312 if (!field || !name ||
313 bt_ctf_field_type_get_type_id(field->type) !=
314 CTF_TYPE_STRUCT) {
315 goto error;
316 }
317
318 field_quark = g_quark_from_string(name);
319 structure = container_of(field, struct bt_ctf_field_structure, parent);
320 structure_type = container_of(field->type,
321 struct bt_ctf_field_type_structure, parent);
322 field_type = bt_ctf_field_type_structure_get_type(structure_type, name);
323 if (!g_hash_table_lookup_extended(structure->field_name_to_index,
324 GUINT_TO_POINTER(field_quark), NULL, (gpointer *)&index)) {
325 goto error;
326 }
327
328 if (structure->fields->pdata[index]) {
329 new_field = structure->fields->pdata[index];
330 goto end;
331 }
332
333 new_field = bt_ctf_field_create(field_type);
334 if (!new_field) {
335 goto error;
336 }
337
338 structure->fields->pdata[index] = new_field;
339end:
340 bt_ctf_field_get(new_field);
341error:
342 return new_field;
343}
344
cd95e351
JG
345struct bt_ctf_field *bt_ctf_field_structure_get_field_by_index(
346 struct bt_ctf_field *field, size_t index)
347{
348 int ret;
349 const char *field_name;
350 struct bt_ctf_field_structure *structure;
351 struct bt_ctf_field_type *structure_type;
352 struct bt_ctf_field_type *field_type = NULL;
353 struct bt_ctf_field *ret_field = NULL;
354
355 if (!field ||
356 bt_ctf_field_type_get_type_id(field->type) != CTF_TYPE_STRUCT) {
357 goto end;
358 }
359
360 structure = container_of(field, struct bt_ctf_field_structure, parent);
361 if (index >= structure->fields->len) {
362 goto error;
363 }
364
365 ret_field = structure->fields->pdata[index];
366 if (ret_field) {
367 goto end;
368 }
369
370 /* Field has not been instanciated yet, create it */
371 structure_type = bt_ctf_field_get_type(field);
372 if (!structure_type) {
373 goto error;
374 }
375
376 ret = bt_ctf_field_type_structure_get_field(structure_type,
377 &field_name, &field_type, index);
378 bt_ctf_field_type_put(structure_type);
379 if (ret) {
380 goto error;
381 }
382
383 ret_field = bt_ctf_field_create(field_type);
384 if (!ret_field) {
385 goto error;
386 }
387
388 structure->fields->pdata[index] = ret_field;
389end:
390 bt_ctf_field_get(ret_field);
391error:
392 if (field_type) {
393 bt_ctf_field_type_put(field_type);
394 }
395 return ret_field;
396}
397
273b65be
JG
398BT_HIDDEN
399int bt_ctf_field_structure_set_field(struct bt_ctf_field *field,
400 const char *name, struct bt_ctf_field *value)
401{
402 int ret = 0;
403 GQuark field_quark;
404 struct bt_ctf_field_structure *structure;
405 struct bt_ctf_field_type_structure *structure_type;
406 struct bt_ctf_field_type *expected_field_type;
407 size_t index;
408
409 if (!field || !name || !value ||
410 bt_ctf_field_type_get_type_id(field->type) !=
411 CTF_TYPE_STRUCT) {
412 ret = -1;
413 goto end;
414 }
415
416 field_quark = g_quark_from_string(name);
417 structure = container_of(field, struct bt_ctf_field_structure, parent);
418 structure_type = container_of(field->type,
419 struct bt_ctf_field_type_structure, parent);
420 expected_field_type = bt_ctf_field_type_structure_get_type(
421 structure_type, name);
422 if (expected_field_type != value->type) {
423 ret = -1;
424 goto end;
425 }
426
427 if (!g_hash_table_lookup_extended(structure->field_name_to_index,
428 GUINT_TO_POINTER(field_quark), NULL, (gpointer *) &index)) {
429 goto end;
430 }
431
432 if (structure->fields->pdata[index]) {
433 bt_ctf_field_put(structure->fields->pdata[index]);
434 }
435
436 structure->fields->pdata[index] = value;
437 bt_ctf_field_get(value);
438end:
439 return ret;
440}
441
442struct bt_ctf_field *bt_ctf_field_array_get_field(struct bt_ctf_field *field,
443 uint64_t index)
444{
445 struct bt_ctf_field *new_field = NULL;
446 struct bt_ctf_field_array *array;
447 struct bt_ctf_field_type_array *array_type;
448 struct bt_ctf_field_type *field_type;
449
450 if (!field || bt_ctf_field_type_get_type_id(field->type) !=
451 CTF_TYPE_ARRAY) {
452 goto end;
453 }
454
455 array = container_of(field, struct bt_ctf_field_array, parent);
456 if (index >= array->elements->len) {
457 goto end;
458 }
459
460 array_type = container_of(field->type, struct bt_ctf_field_type_array,
461 parent);
462 field_type = bt_ctf_field_type_array_get_element_type(array_type);
463 if (array->elements->pdata[(size_t)index]) {
464 new_field = array->elements->pdata[(size_t)index];
465 goto end;
466 }
467
468 new_field = bt_ctf_field_create(field_type);
469 bt_ctf_field_get(new_field);
470 array->elements->pdata[(size_t)index] = new_field;
471end:
472 return new_field;
473}
474
475struct bt_ctf_field *bt_ctf_field_sequence_get_field(struct bt_ctf_field *field,
476 uint64_t index)
477{
478 struct bt_ctf_field *new_field = NULL;
479 struct bt_ctf_field_sequence *sequence;
480 struct bt_ctf_field_type_sequence *sequence_type;
481 struct bt_ctf_field_type *field_type;
482
483 if (!field || bt_ctf_field_type_get_type_id(field->type) !=
484 CTF_TYPE_SEQUENCE) {
485 goto end;
486 }
487
488 sequence = container_of(field, struct bt_ctf_field_sequence, parent);
489 if (!sequence->elements || sequence->elements->len <= index) {
490 goto end;
491 }
492
493 sequence_type = container_of(field->type,
494 struct bt_ctf_field_type_sequence, parent);
495 field_type = bt_ctf_field_type_sequence_get_element_type(sequence_type);
496 if (sequence->elements->pdata[(size_t)index]) {
497 new_field = sequence->elements->pdata[(size_t)index];
498 goto end;
499 }
500
501 new_field = bt_ctf_field_create(field_type);
502 bt_ctf_field_get(new_field);
503 sequence->elements->pdata[(size_t)index] = new_field;
504end:
505 return new_field;
506}
507
508struct bt_ctf_field *bt_ctf_field_variant_get_field(struct bt_ctf_field *field,
509 struct bt_ctf_field *tag_field)
510{
511 struct bt_ctf_field *new_field = NULL;
512 struct bt_ctf_field_variant *variant;
513 struct bt_ctf_field_type_variant *variant_type;
514 struct bt_ctf_field_type *field_type;
515 struct bt_ctf_field *tag_enum = NULL;
516 struct bt_ctf_field_integer *tag_enum_integer;
517 int64_t tag_enum_value;
518
519 if (!field || !tag_field ||
520 bt_ctf_field_type_get_type_id(field->type) !=
521 CTF_TYPE_VARIANT ||
522 bt_ctf_field_type_get_type_id(tag_field->type) !=
523 CTF_TYPE_ENUM) {
524 goto end;
525 }
526
527 variant = container_of(field, struct bt_ctf_field_variant, parent);
528 variant_type = container_of(field->type,
529 struct bt_ctf_field_type_variant, parent);
530 tag_enum = bt_ctf_field_enumeration_get_container(tag_field);
531 if (!tag_enum) {
532 goto end;
533 }
534
535 tag_enum_integer = container_of(tag_enum, struct bt_ctf_field_integer,
536 parent);
537
538 if (!bt_ctf_field_validate(variant->tag)) {
539 goto end;
540 }
541
542 tag_enum_value = tag_enum_integer->definition.value._signed;
543 field_type = bt_ctf_field_type_variant_get_field_type(variant_type,
544 tag_enum_value);
545 if (!field_type) {
546 goto end;
547 }
548
549 new_field = bt_ctf_field_create(field_type);
550 if (!new_field) {
551 goto end;
552 }
553
554 bt_ctf_field_put(variant->tag);
555 bt_ctf_field_put(variant->payload);
556 bt_ctf_field_get(new_field);
557 bt_ctf_field_get(tag_field);
558 variant->tag = tag_field;
559 variant->payload = new_field;
560end:
561 bt_ctf_field_put(tag_enum);
562 return new_field;
563}
564
565struct bt_ctf_field *bt_ctf_field_enumeration_get_container(
566 struct bt_ctf_field *field)
567{
568 struct bt_ctf_field *container = NULL;
569 struct bt_ctf_field_enumeration *enumeration;
570
571 if (!field) {
572 goto end;
573 }
574
575 enumeration = container_of(field, struct bt_ctf_field_enumeration,
576 parent);
577 if (!enumeration->payload) {
578 struct bt_ctf_field_type_enumeration *enumeration_type =
579 container_of(field->type,
580 struct bt_ctf_field_type_enumeration, parent);
581 enumeration->payload =
582 bt_ctf_field_create(enumeration_type->container);
583 }
584
585 container = enumeration->payload;
586 bt_ctf_field_get(container);
587end:
588 return container;
589}
590
cd95e351
JG
591const char *bt_ctf_field_enumeration_get_mapping_name(
592 struct bt_ctf_field *field)
593{
594 int ret;
595 const char *name = NULL;
596 struct bt_ctf_field *container = NULL;
597 struct bt_ctf_field_type *container_type = NULL;
598 struct bt_ctf_field_type_integer *integer_type = NULL;
599 struct bt_ctf_field_type_enumeration *enumeration_type = NULL;
600
601 container = bt_ctf_field_enumeration_get_container(field);
602 if (!container) {
603 goto end;
604 }
605
606 container_type = bt_ctf_field_get_type(container);
607 if (!container_type) {
608 goto error_put_container;
609 }
610
611 integer_type = container_of(container_type,
612 struct bt_ctf_field_type_integer, parent);
613 enumeration_type = container_of(field->type,
614 struct bt_ctf_field_type_enumeration, parent);
615
10817e06 616 if (!integer_type->declaration.signedness) {
cd95e351
JG
617 uint64_t value;
618 ret = bt_ctf_field_unsigned_integer_get_value(container,
619 &value);
620 if (ret) {
621 goto error_put_container_type;
622 }
623
624 name = bt_ctf_field_type_enumeration_get_mapping_name_unsigned(
625 enumeration_type, value);
626 } else {
627 int64_t value;
628 ret = bt_ctf_field_signed_integer_get_value(container,
629 &value);
630 if (ret) {
631 goto error_put_container_type;
632 }
633
634 name = bt_ctf_field_type_enumeration_get_mapping_name_signed(
635 enumeration_type, value);
636 }
637
638error_put_container_type:
639 bt_ctf_field_type_put(container_type);
640error_put_container:
641 bt_ctf_field_put(container);
642end:
643 return name;
644}
645
646int bt_ctf_field_signed_integer_get_value(struct bt_ctf_field *field,
647 int64_t *value)
648{
649 int ret = 0;
650 struct bt_ctf_field_integer *integer;
651 struct bt_ctf_field_type_integer *integer_type;
652
653 if (!field || !value || !field->payload_set ||
654 bt_ctf_field_type_get_type_id(field->type) !=
655 CTF_TYPE_INTEGER) {
656 ret = -1;
657 goto end;
658 }
659
660 integer_type = container_of(field->type,
661 struct bt_ctf_field_type_integer, parent);
662 if (!integer_type->declaration.signedness) {
663 ret = -1;
664 goto end;
665 }
666
667 integer = container_of(field,
668 struct bt_ctf_field_integer, parent);
669 *value = integer->definition.value._signed;
670end:
671 return ret;
672}
673
273b65be
JG
674int bt_ctf_field_signed_integer_set_value(struct bt_ctf_field *field,
675 int64_t value)
676{
677 int ret = 0;
678 struct bt_ctf_field_integer *integer;
679 struct bt_ctf_field_type_integer *integer_type;
680 unsigned int size;
681 int64_t min_value, max_value;
682
683 if (!field ||
684 bt_ctf_field_type_get_type_id(field->type) !=
685 CTF_TYPE_INTEGER) {
686 ret = -1;
687 goto end;
688 }
689
690 integer = container_of(field, struct bt_ctf_field_integer, parent);
691 integer_type = container_of(field->type,
692 struct bt_ctf_field_type_integer, parent);
693 if (!integer_type->declaration.signedness) {
694 ret = -1;
695 goto end;
696 }
697
698 size = integer_type->declaration.len;
699 min_value = -((int64_t)1 << (size - 1));
700 max_value = ((int64_t)1 << (size - 1)) - 1;
701 if (value < min_value || value > max_value) {
702 ret = -1;
703 goto end;
704 }
705
706 integer->definition.value._signed = value;
707 integer->parent.payload_set = 1;
708end:
709 return ret;
710}
711
cd95e351
JG
712int bt_ctf_field_unsigned_integer_get_value(struct bt_ctf_field *field,
713 uint64_t *value)
714{
715 int ret = 0;
716 struct bt_ctf_field_integer *integer;
717 struct bt_ctf_field_type_integer *integer_type;
718
719 if (!field || !value || !field->payload_set ||
720 bt_ctf_field_type_get_type_id(field->type) !=
721 CTF_TYPE_INTEGER) {
722 ret = -1;
723 goto end;
724 }
725
726 integer_type = container_of(field->type,
727 struct bt_ctf_field_type_integer, parent);
728 if (integer_type->declaration.signedness) {
729 ret = -1;
730 goto end;
731 }
732
733 integer = container_of(field,
734 struct bt_ctf_field_integer, parent);
735 *value = integer->definition.value._unsigned;
736end:
737 return ret;
738}
739
273b65be
JG
740int bt_ctf_field_unsigned_integer_set_value(struct bt_ctf_field *field,
741 uint64_t value)
742{
743 int ret = 0;
744 struct bt_ctf_field_integer *integer;
745 struct bt_ctf_field_type_integer *integer_type;
746 unsigned int size;
747 uint64_t max_value;
748
749 if (!field ||
750 bt_ctf_field_type_get_type_id(field->type) !=
751 CTF_TYPE_INTEGER) {
752 ret = -1;
753 goto end;
754 }
755
756 integer = container_of(field, struct bt_ctf_field_integer, parent);
757 integer_type = container_of(field->type,
758 struct bt_ctf_field_type_integer, parent);
759 if (integer_type->declaration.signedness) {
760 ret = -1;
761 goto end;
762 }
763
764 size = integer_type->declaration.len;
765 max_value = (size == 64) ? UINT64_MAX : ((uint64_t)1 << size) - 1;
766 if (value > max_value) {
767 ret = -1;
768 goto end;
769 }
770
771 integer->definition.value._unsigned = value;
772 integer->parent.payload_set = 1;
773end:
774 return ret;
775}
776
cd95e351
JG
777int bt_ctf_field_floating_point_get_value(struct bt_ctf_field *field,
778 double *value)
779{
780 int ret = 0;
781 struct bt_ctf_field_floating_point *floating_point;
782
783 if (!field || !value || !field->payload_set ||
784 bt_ctf_field_type_get_type_id(field->type) !=
785 CTF_TYPE_FLOAT) {
786 ret = -1;
787 goto end;
788 }
789
790 floating_point = container_of(field,
791 struct bt_ctf_field_floating_point, parent);
792 *value = floating_point->definition.value;
793end:
794 return ret;
795}
796
273b65be
JG
797int bt_ctf_field_floating_point_set_value(struct bt_ctf_field *field,
798 double value)
799{
800 int ret = 0;
801 struct bt_ctf_field_floating_point *floating_point;
802
803 if (!field ||
804 bt_ctf_field_type_get_type_id(field->type) !=
805 CTF_TYPE_FLOAT) {
806 ret = -1;
807 goto end;
808 }
809 floating_point = container_of(field, struct bt_ctf_field_floating_point,
810 parent);
811 floating_point->definition.value = value;
812 floating_point->parent.payload_set = 1;
813end:
814 return ret;
815}
816
cd95e351
JG
817const char *bt_ctf_field_string_get_value(struct bt_ctf_field *field)
818{
819 const char *ret = NULL;
820 struct bt_ctf_field_string *string;
821
822 if (!field || !field->payload_set ||
823 bt_ctf_field_type_get_type_id(field->type) !=
824 CTF_TYPE_STRING) {
825 goto end;
826 }
827
828 string = container_of(field,
829 struct bt_ctf_field_string, parent);
830 ret = string->payload->str;
831end:
832 return ret;
833}
834
273b65be
JG
835int bt_ctf_field_string_set_value(struct bt_ctf_field *field,
836 const char *value)
837{
838 int ret = 0;
839 struct bt_ctf_field_string *string;
840
841 if (!field || !value ||
842 bt_ctf_field_type_get_type_id(field->type) !=
843 CTF_TYPE_STRING) {
844 ret = -1;
845 goto end;
846 }
847
848 string = container_of(field, struct bt_ctf_field_string, parent);
849 if (string->payload) {
850 g_string_free(string->payload, TRUE);
851 }
852
853 string->payload = g_string_new(value);
854 string->parent.payload_set = 1;
855end:
856 return ret;
857}
858
859BT_HIDDEN
860int bt_ctf_field_validate(struct bt_ctf_field *field)
861{
862 int ret = 0;
863 enum ctf_type_id type_id;
864
865 if (!field) {
866 ret = -1;
867 goto end;
868 }
869
870 type_id = bt_ctf_field_type_get_type_id(field->type);
871 if (type_id <= CTF_TYPE_UNKNOWN || type_id >= NR_CTF_TYPES) {
872 ret = -1;
873 goto end;
874 }
875
876 ret = field_validate_funcs[type_id](field);
877end:
878 return ret;
879}
880
881BT_HIDDEN
882int bt_ctf_field_serialize(struct bt_ctf_field *field,
883 struct ctf_stream_pos *pos)
884{
885 int ret = 0;
886 enum ctf_type_id type_id;
887
888 if (!field || !pos) {
889 ret = -1;
890 goto end;
891 }
892
893 type_id = bt_ctf_field_type_get_type_id(field->type);
894 if (type_id <= CTF_TYPE_UNKNOWN || type_id >= NR_CTF_TYPES) {
895 ret = -1;
896 goto end;
897 }
898
899 ret = field_serialize_funcs[type_id](field, pos);
900end:
901 return ret;
902}
903
904static
905struct bt_ctf_field *bt_ctf_field_integer_create(struct bt_ctf_field_type *type)
906{
907 struct bt_ctf_field_type_integer *integer_type = container_of(type,
908 struct bt_ctf_field_type_integer, parent);
909 struct bt_ctf_field_integer *integer = g_new0(
910 struct bt_ctf_field_integer, 1);
911
912 if (integer) {
913 integer->definition.declaration = &integer_type->declaration;
914 }
915
916 return integer ? &integer->parent : NULL;
917}
918
919static
920struct bt_ctf_field *bt_ctf_field_enumeration_create(
921 struct bt_ctf_field_type *type)
922{
923 struct bt_ctf_field_enumeration *enumeration = g_new0(
924 struct bt_ctf_field_enumeration, 1);
925
926 return enumeration ? &enumeration->parent : NULL;
927}
928
929static
930struct bt_ctf_field *bt_ctf_field_floating_point_create(
931 struct bt_ctf_field_type *type)
932{
933 struct bt_ctf_field_floating_point *floating_point;
934 struct bt_ctf_field_type_floating_point *floating_point_type;
935
936 floating_point = g_new0(struct bt_ctf_field_floating_point, 1);
937 if (!floating_point) {
938 goto end;
939 }
940
941 floating_point_type = container_of(type,
942 struct bt_ctf_field_type_floating_point, parent);
943 floating_point->definition.declaration = container_of(
944 type->declaration, struct declaration_float, p);
945
946
947 floating_point->definition.sign = &floating_point->sign;
948 floating_point->sign.declaration = &floating_point_type->sign;
949 floating_point->definition.sign->p.declaration =
950 &floating_point_type->sign.p;
951
952 floating_point->definition.mantissa = &floating_point->mantissa;
953 floating_point->mantissa.declaration = &floating_point_type->mantissa;
954 floating_point->definition.mantissa->p.declaration =
955 &floating_point_type->mantissa.p;
956
957 floating_point->definition.exp = &floating_point->exp;
958 floating_point->exp.declaration = &floating_point_type->exp;
959 floating_point->definition.exp->p.declaration =
960 &floating_point_type->exp.p;
961
962end:
963 return floating_point ? &floating_point->parent : NULL;
964}
965
966static
967struct bt_ctf_field *bt_ctf_field_structure_create(
968 struct bt_ctf_field_type *type)
969{
970 struct bt_ctf_field_type_structure *structure_type = container_of(type,
971 struct bt_ctf_field_type_structure, parent);
972 struct bt_ctf_field_structure *structure = g_new0(
973 struct bt_ctf_field_structure, 1);
974 struct bt_ctf_field *field = NULL;
975
976 if (!structure || !structure_type->fields->len) {
977 goto end;
978 }
979
980 structure->field_name_to_index = structure_type->field_name_to_index;
981 structure->fields = g_ptr_array_new_with_free_func(
982 (GDestroyNotify)bt_ctf_field_put);
983 g_ptr_array_set_size(structure->fields,
984 g_hash_table_size(structure->field_name_to_index));
985 field = &structure->parent;
986end:
987 return field;
988}
989
990static
991struct bt_ctf_field *bt_ctf_field_variant_create(struct bt_ctf_field_type *type)
992{
993 struct bt_ctf_field_variant *variant = g_new0(
994 struct bt_ctf_field_variant, 1);
995 return variant ? &variant->parent : NULL;
996}
997
998static
999struct bt_ctf_field *bt_ctf_field_array_create(struct bt_ctf_field_type *type)
1000{
1001 struct bt_ctf_field_array *array = g_new0(struct bt_ctf_field_array, 1);
1002 struct bt_ctf_field_type_array *array_type;
1003 unsigned int array_length;
1004
1005 if (!array || !type) {
1006 goto error;
1007 }
1008
1009 array_type = container_of(type, struct bt_ctf_field_type_array, parent);
1010 array_length = array_type->length;
fe0fe95c 1011 array->elements = g_ptr_array_sized_new(array_length);
273b65be
JG
1012 if (!array->elements) {
1013 goto error;
1014 }
1015
fe0fe95c
JG
1016 g_ptr_array_set_free_func(array->elements,
1017 (GDestroyNotify)bt_ctf_field_put);
273b65be
JG
1018 g_ptr_array_set_size(array->elements, array_length);
1019 return &array->parent;
1020error:
1021 g_free(array);
1022 return NULL;
1023}
1024
1025static
1026struct bt_ctf_field *bt_ctf_field_sequence_create(
1027 struct bt_ctf_field_type *type)
1028{
1029 struct bt_ctf_field_sequence *sequence = g_new0(
1030 struct bt_ctf_field_sequence, 1);
1031 return sequence ? &sequence->parent : NULL;
1032}
1033
1034static
1035struct bt_ctf_field *bt_ctf_field_string_create(struct bt_ctf_field_type *type)
1036{
1037 struct bt_ctf_field_string *string = g_new0(
1038 struct bt_ctf_field_string, 1);
1039 return string ? &string->parent : NULL;
1040}
1041
1042static
1043void bt_ctf_field_destroy(struct bt_ctf_ref *ref)
1044{
1045 struct bt_ctf_field *field;
1046 struct bt_ctf_field_type *type;
1047 enum ctf_type_id type_id;
1048
1049 if (!ref) {
1050 return;
1051 }
1052
1053 field = container_of(ref, struct bt_ctf_field, ref_count);
1054 type = field->type;
1055 type_id = bt_ctf_field_type_get_type_id(type);
1056 if (type_id <= CTF_TYPE_UNKNOWN ||
1057 type_id >= NR_CTF_TYPES) {
1058 return;
1059 }
1060
1061 field_destroy_funcs[type_id](field);
1062 if (type) {
1063 bt_ctf_field_type_put(type);
1064 }
1065}
1066
1067static
1068void bt_ctf_field_integer_destroy(struct bt_ctf_field *field)
1069{
1070 struct bt_ctf_field_integer *integer;
1071
1072 if (!field) {
1073 return;
1074 }
1075
1076 integer = container_of(field, struct bt_ctf_field_integer, parent);
1077 g_free(integer);
1078}
1079
1080static
1081void bt_ctf_field_enumeration_destroy(struct bt_ctf_field *field)
1082{
1083 struct bt_ctf_field_enumeration *enumeration;
1084
1085 if (!field) {
1086 return;
1087 }
1088
1089 enumeration = container_of(field, struct bt_ctf_field_enumeration,
1090 parent);
1091 bt_ctf_field_put(enumeration->payload);
1092 g_free(enumeration);
1093}
1094
1095static
1096void bt_ctf_field_floating_point_destroy(struct bt_ctf_field *field)
1097{
1098 struct bt_ctf_field_floating_point *floating_point;
1099
1100 if (!field) {
1101 return;
1102 }
1103
1104 floating_point = container_of(field, struct bt_ctf_field_floating_point,
1105 parent);
1106 g_free(floating_point);
1107}
1108
1109static
1110void bt_ctf_field_structure_destroy(struct bt_ctf_field *field)
1111{
1112 struct bt_ctf_field_structure *structure;
1113
1114 if (!field) {
1115 return;
1116 }
1117
1118 structure = container_of(field, struct bt_ctf_field_structure, parent);
1119 g_ptr_array_free(structure->fields, TRUE);
1120 g_free(structure);
1121}
1122
1123static
1124void bt_ctf_field_variant_destroy(struct bt_ctf_field *field)
1125{
1126 struct bt_ctf_field_variant *variant;
1127
1128 if (!field) {
1129 return;
1130 }
1131
1132 variant = container_of(field, struct bt_ctf_field_variant, parent);
1133 bt_ctf_field_put(variant->tag);
1134 bt_ctf_field_put(variant->payload);
1135 g_free(variant);
1136}
1137
1138static
1139void bt_ctf_field_array_destroy(struct bt_ctf_field *field)
1140{
1141 struct bt_ctf_field_array *array;
1142
1143 if (!field) {
1144 return;
1145 }
1146
1147 array = container_of(field, struct bt_ctf_field_array, parent);
1148 g_ptr_array_free(array->elements, TRUE);
1149 g_free(array);
1150}
1151
1152static
1153void bt_ctf_field_sequence_destroy(struct bt_ctf_field *field)
1154{
1155 struct bt_ctf_field_sequence *sequence;
1156
1157 if (!field) {
1158 return;
1159 }
1160
1161 sequence = container_of(field, struct bt_ctf_field_sequence, parent);
1162 g_ptr_array_free(sequence->elements, TRUE);
1163 bt_ctf_field_put(sequence->length);
1164 g_free(sequence);
1165}
1166
1167static
1168void bt_ctf_field_string_destroy(struct bt_ctf_field *field)
1169{
1170 struct bt_ctf_field_string *string;
1171 if (!field) {
1172 return;
1173 }
1174
1175 string = container_of(field, struct bt_ctf_field_string, parent);
1176 g_string_free(string->payload, TRUE);
1177 g_free(string);
1178}
1179
1180static
1181int bt_ctf_field_generic_validate(struct bt_ctf_field *field)
1182{
da2f6971 1183 return (field && field->payload_set) ? 0 : -1;
273b65be
JG
1184}
1185
1186static
1187int bt_ctf_field_enumeration_validate(struct bt_ctf_field *field)
1188{
1189 int ret;
1190 struct bt_ctf_field_enumeration *enumeration;
1191
1192 if (!field) {
1193 ret = -1;
1194 goto end;
1195 }
1196
1197 enumeration = container_of(field, struct bt_ctf_field_enumeration,
1198 parent);
1199 if (!enumeration->payload) {
1200 ret = -1;
1201 goto end;
1202 }
1203
1204 ret = bt_ctf_field_validate(enumeration->payload);
1205end:
1206 return ret;
1207}
1208
1209static
1210int bt_ctf_field_structure_validate(struct bt_ctf_field *field)
1211{
1212 size_t i;
1213 int ret = 0;
1214 struct bt_ctf_field_structure *structure;
1215
1216 if (!field) {
1217 ret = -1;
1218 goto end;
1219 }
1220
1221 structure = container_of(field, struct bt_ctf_field_structure, parent);
1222 for (i = 0; i < structure->fields->len; i++) {
1223 ret = bt_ctf_field_validate(structure->fields->pdata[i]);
1224 if (ret) {
1225 goto end;
1226 }
1227 }
1228end:
1229 return ret;
1230}
1231
1232static
1233int bt_ctf_field_variant_validate(struct bt_ctf_field *field)
1234{
1235 int ret = 0;
1236 struct bt_ctf_field_variant *variant;
1237
1238 if (!field) {
1239 ret = -1;
1240 goto end;
1241 }
1242
1243 variant = container_of(field, struct bt_ctf_field_variant, parent);
1244 ret = bt_ctf_field_validate(variant->payload);
1245end:
1246 return ret;
1247}
1248
1249static
1250int bt_ctf_field_array_validate(struct bt_ctf_field *field)
1251{
1252 size_t i;
1253 int ret = 0;
1254 struct bt_ctf_field_array *array;
1255
1256 if (!field) {
1257 ret = -1;
1258 goto end;
1259 }
1260
1261 array = container_of(field, struct bt_ctf_field_array, parent);
1262 for (i = 0; i < array->elements->len; i++) {
1263 ret = bt_ctf_field_validate(array->elements->pdata[i]);
1264 if (ret) {
1265 goto end;
1266 }
1267 }
1268end:
1269 return ret;
1270}
1271
1272static
1273int bt_ctf_field_sequence_validate(struct bt_ctf_field *field)
1274{
1275 size_t i;
1276 int ret = 0;
1277 struct bt_ctf_field_sequence *sequence;
1278
1279 if (!field) {
1280 ret = -1;
1281 goto end;
1282 }
1283
1284 sequence = container_of(field, struct bt_ctf_field_sequence, parent);
1285 for (i = 0; i < sequence->elements->len; i++) {
1286 ret = bt_ctf_field_validate(sequence->elements->pdata[i]);
1287 if (ret) {
1288 goto end;
1289 }
1290 }
1291end:
1292 return ret;
1293}
1294
1295static
1296int bt_ctf_field_integer_serialize(struct bt_ctf_field *field,
1297 struct ctf_stream_pos *pos)
1298{
1299 int ret = 0;
1300 struct bt_ctf_field_integer *integer = container_of(field,
1301 struct bt_ctf_field_integer, parent);
1302
1303retry:
1304 ret = ctf_integer_write(&pos->parent, &integer->definition.p);
1305 if (ret == -EFAULT) {
1306 /*
1307 * The field is too large to fit in the current packet's
1308 * remaining space. Bump the packet size and retry.
1309 */
1310 ret = increase_packet_size(pos);
1311 if (ret) {
1312 goto end;
1313 }
1314 goto retry;
1315 }
1316end:
1317 return ret;
1318}
1319
1320static
1321int bt_ctf_field_enumeration_serialize(struct bt_ctf_field *field,
1322 struct ctf_stream_pos *pos)
1323{
1324 struct bt_ctf_field_enumeration *enumeration = container_of(
1325 field, struct bt_ctf_field_enumeration, parent);
1326
1327 return bt_ctf_field_serialize(enumeration->payload, pos);
1328}
1329
1330static
1331int bt_ctf_field_floating_point_serialize(struct bt_ctf_field *field,
1332 struct ctf_stream_pos *pos)
1333{
1334 int ret = 0;
1335 struct bt_ctf_field_floating_point *floating_point = container_of(field,
1336 struct bt_ctf_field_floating_point, parent);
1337
1338retry:
1339 ret = ctf_float_write(&pos->parent, &floating_point->definition.p);
1340 if (ret == -EFAULT) {
1341 /*
1342 * The field is too large to fit in the current packet's
1343 * remaining space. Bump the packet size and retry.
1344 */
1345 ret = increase_packet_size(pos);
1346 if (ret) {
1347 goto end;
1348 }
1349 goto retry;
1350 }
1351end:
1352 return ret;
1353}
1354
1355static
1356int bt_ctf_field_structure_serialize(struct bt_ctf_field *field,
1357 struct ctf_stream_pos *pos)
1358{
1359 size_t i;
1360 int ret = 0;
1361 struct bt_ctf_field_structure *structure = container_of(
1362 field, struct bt_ctf_field_structure, parent);
1363
1364 while (!ctf_pos_access_ok(pos,
1365 offset_align(pos->offset,
1366 field->type->declaration->alignment))) {
9f56e450
JG
1367 ret = increase_packet_size(pos);
1368 if (ret) {
1369 goto end;
1370 }
273b65be
JG
1371 }
1372
70fd5a51
MD
1373 if (!ctf_align_pos(pos, field->type->declaration->alignment)) {
1374 ret = -1;
1375 goto end;
1376 }
273b65be
JG
1377
1378 for (i = 0; i < structure->fields->len; i++) {
1379 struct bt_ctf_field *field = g_ptr_array_index(
1380 structure->fields, i);
1381
1382 ret = bt_ctf_field_serialize(field, pos);
1383 if (ret) {
1384 break;
1385 }
1386 }
9f56e450 1387end:
273b65be
JG
1388 return ret;
1389}
1390
1391static
1392int bt_ctf_field_variant_serialize(struct bt_ctf_field *field,
1393 struct ctf_stream_pos *pos)
1394{
1395 struct bt_ctf_field_variant *variant = container_of(
1396 field, struct bt_ctf_field_variant, parent);
1397
1398 return bt_ctf_field_serialize(variant->payload, pos);
1399}
1400
1401static
1402int bt_ctf_field_array_serialize(struct bt_ctf_field *field,
1403 struct ctf_stream_pos *pos)
1404{
1405 size_t i;
1406 int ret = 0;
1407 struct bt_ctf_field_array *array = container_of(
1408 field, struct bt_ctf_field_array, parent);
1409
1410 for (i = 0; i < array->elements->len; i++) {
1411 ret = bt_ctf_field_serialize(
1412 g_ptr_array_index(array->elements, i), pos);
1413 if (ret) {
1414 goto end;
1415 }
1416 }
1417end:
1418 return ret;
1419}
1420
1421static
1422int bt_ctf_field_sequence_serialize(struct bt_ctf_field *field,
1423 struct ctf_stream_pos *pos)
1424{
1425 size_t i;
1426 int ret = 0;
1427 struct bt_ctf_field_sequence *sequence = container_of(
1428 field, struct bt_ctf_field_sequence, parent);
1429
1430 for (i = 0; i < sequence->elements->len; i++) {
1431 ret = bt_ctf_field_serialize(
1432 g_ptr_array_index(sequence->elements, i), pos);
1433 if (ret) {
1434 goto end;
1435 }
1436 }
1437end:
1438 return ret;
1439}
1440
1441static
1442int bt_ctf_field_string_serialize(struct bt_ctf_field *field,
1443 struct ctf_stream_pos *pos)
1444{
1445 size_t i;
1446 int ret = 0;
1447 struct bt_ctf_field_string *string = container_of(field,
1448 struct bt_ctf_field_string, parent);
1449 struct bt_ctf_field_type *character_type =
1450 get_field_type(FIELD_TYPE_ALIAS_UINT8_T);
1451 struct bt_ctf_field *character = bt_ctf_field_create(character_type);
1452
1453 for (i = 0; i < string->payload->len + 1; i++) {
1454 ret = bt_ctf_field_unsigned_integer_set_value(character,
1455 (uint64_t) string->payload->str[i]);
1456 if (ret) {
1457 goto end;
1458 }
1459
1460 ret = bt_ctf_field_integer_serialize(character, pos);
1461 if (ret) {
1462 goto end;
1463 }
1464 }
1465end:
1466 bt_ctf_field_put(character);
1467 bt_ctf_field_type_put(character_type);
1468 return ret;
1469}
1470
1471static
1472int increase_packet_size(struct ctf_stream_pos *pos)
1473{
1474 int ret;
1475
1476 assert(pos);
1477 ret = munmap_align(pos->base_mma);
1478 if (ret) {
1479 goto end;
1480 }
1481
1482 pos->packet_size += PACKET_LEN_INCREMENT;
1483 ret = posix_fallocate(pos->fd, pos->mmap_offset,
1484 pos->packet_size / CHAR_BIT);
1485 if (ret) {
1486 goto end;
1487 }
1488
1489 pos->base_mma = mmap_align(pos->packet_size / CHAR_BIT, pos->prot,
1490 pos->flags, pos->fd, pos->mmap_offset);
1491 if (pos->base_mma == MAP_FAILED) {
1492 ret = -1;
1493 }
1494end:
1495 return ret;
1496}
This page took 0.089444 seconds and 4 git commands to generate.