2 * SPDX-License-Identifier: MIT
4 * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
5 * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
7 * Babeltrace - CTF binary field class reader (BFCR)
10 #include "common/align.h"
11 #include "common/assert.h"
12 #include "common/common.h"
13 #include "compat/bitfield.h"
14 #include "cpp-common/bt2c/logging.hpp"
15 #include "cpp-common/vendor/fmt/format.h"
17 #include "../metadata/tsdl/ctf-meta.hpp"
20 #define DIV8(_x) ((_x) >> 3)
21 #define BYTES_TO_BITS(_x) ((_x) *8)
22 #define BITS_TO_BYTES_FLOOR(_x) DIV8(_x)
23 #define BITS_TO_BYTES_CEIL(_x) DIV8((_x) + 7)
24 #define IN_BYTE_OFFSET(_at) ((_at) &7)
26 /* A visit stack entry */
30 * Current class of base field, one of:
37 struct ctf_field_class
*base_class
;
39 /* Length of base field (always 1 for a variant class) */
42 /* Index of next field to read */
51 /* Entries (struct stack_entry) */
54 /* Number of active entries */
61 BFCR_STATE_NEXT_FIELD
,
62 BFCR_STATE_ALIGN_BASIC
,
63 BFCR_STATE_ALIGN_COMPOUND
,
64 BFCR_STATE_READ_BASIC_BEGIN
,
65 BFCR_STATE_READ_BASIC_CONTINUE
,
69 static const char *format_as(bfcr_state state
) noexcept
72 case BFCR_STATE_NEXT_FIELD
:
75 case BFCR_STATE_ALIGN_BASIC
:
78 case BFCR_STATE_ALIGN_COMPOUND
:
79 return "ALIGN_COMPOUND";
81 case BFCR_STATE_READ_BASIC_BEGIN
:
82 return "READ_BASIC_BEGIN";
84 case BFCR_STATE_READ_BASIC_CONTINUE
:
85 return "READ_BASIC_CONTINUE";
94 /* Binary class reader */
97 explicit bt_bfcr(const bt2c::Logger
& parentLogger
) : logger
{parentLogger
, "PLUGIN/CTF/BFCR"}
104 struct stack
*stack
= nullptr;
106 /* Current basic field class */
107 struct ctf_field_class
*cur_basic_field_class
= nullptr;
110 enum bfcr_state state
= static_cast<bfcr_state
>(0);
113 * Last basic field class's byte order.
115 * This is used to detect errors since two contiguous basic
116 * classes for which the common boundary is not the boundary of
117 * a byte cannot have different byte orders.
119 * This is set to CTF_BYTE_ORDER_UNKNOWN on reset and when the last
120 * basic field class was a string class.
122 enum ctf_byte_order last_bo
= CTF_BYTE_ORDER_UNKNOWN
;
124 /* Current byte order (copied to last_bo after a successful read) */
125 enum ctf_byte_order cur_bo
= CTF_BYTE_ORDER_UNKNOWN
;
127 /* Stitch buffer infos */
133 /* Offset, within stitch buffer, of first bit */
136 /* Length (bits) of data in stitch buffer from offset */
140 /* User buffer infos */
144 const uint8_t *addr
= nullptr;
146 /* Offset of data from address (bits) */
149 /* Current position from offset (bits) */
152 /* Offset of offset within whole packet (bits) */
153 size_t packet_offset
= 0;
155 /* Data size in buffer (bits) */
158 /* Buffer size (bytes) */
165 /* Callback functions */
169 void *data
= nullptr;
173 static struct stack
*stack_new(struct bt_bfcr
*bfcr
)
175 struct stack
*stack
= NULL
;
177 stack
= g_new0(struct stack
, 1);
179 BT_CPPLOGE_STR_SPEC(bfcr
->logger
, "Failed to allocate one stack.");
184 stack
->entries
= g_array_new(FALSE
, TRUE
, sizeof(struct stack_entry
));
185 if (!stack
->entries
) {
186 BT_CPPLOGE_STR_SPEC(bfcr
->logger
, "Failed to allocate a GArray.");
190 BT_CPPLOGD_SPEC(bfcr
->logger
, "Created stack: addr={}", fmt::ptr(stack
));
198 static void stack_destroy(struct stack
*stack
)
200 struct bt_bfcr
*bfcr
;
207 BT_CPPLOGD_SPEC(bfcr
->logger
, "Destroying stack: addr={}", fmt::ptr(stack
));
209 if (stack
->entries
) {
210 g_array_free(stack
->entries
, TRUE
);
216 static int stack_push(struct stack
*stack
, struct ctf_field_class
*base_class
, size_t base_len
)
218 struct stack_entry
*entry
;
219 struct bt_bfcr
*bfcr
;
221 BT_ASSERT_DBG(stack
);
222 BT_ASSERT_DBG(base_class
);
224 BT_CPPLOGT_SPEC(bfcr
->logger
,
225 "Pushing field class on stack: stack-addr={}, "
226 "fc-addr={}, fc-type={}, base-length={}, "
227 "stack-size-before={}, stack-size-after={}",
228 fmt::ptr(stack
), fmt::ptr(base_class
), (int) base_class
->type
, base_len
,
229 stack
->size
, stack
->size
+ 1);
231 if (stack
->entries
->len
== stack
->size
) {
232 g_array_set_size(stack
->entries
, stack
->size
+ 1);
235 entry
= &bt_g_array_index(stack
->entries
, struct stack_entry
, stack
->size
);
236 entry
->base_class
= base_class
;
237 entry
->base_len
= base_len
;
243 static inline int64_t get_compound_field_class_length(struct bt_bfcr
*bfcr
,
244 struct ctf_field_class
*fc
)
249 case CTF_FIELD_CLASS_TYPE_STRUCT
:
251 ctf_field_class_struct
*struct_fc
= ctf_field_class_as_struct(fc
);
253 length
= (int64_t) struct_fc
->members
->len
;
256 case CTF_FIELD_CLASS_TYPE_VARIANT
:
258 /* Variant field classes always "contain" a single class */
262 case CTF_FIELD_CLASS_TYPE_ARRAY
:
264 struct ctf_field_class_array
*array_fc
= ctf_field_class_as_array(fc
);
266 length
= (int64_t) array_fc
->length
;
269 case CTF_FIELD_CLASS_TYPE_SEQUENCE
:
270 length
= bfcr
->user
.cbs
.query
.get_sequence_length(fc
, bfcr
->user
.data
);
279 static int stack_push_with_len(struct bt_bfcr
*bfcr
, struct ctf_field_class
*base_class
)
282 int64_t length
= get_compound_field_class_length(bfcr
, base_class
);
285 BT_CPPLOGW_SPEC(bfcr
->logger
,
286 "Cannot get compound field class's field count: "
287 "bfcr-addr={}, fc-addr={}, fc-type={}",
288 fmt::ptr(bfcr
), fmt::ptr(base_class
), (int) base_class
->type
);
289 ret
= BT_BFCR_STATUS_ERROR
;
293 ret
= stack_push(bfcr
->stack
, base_class
, (size_t) length
);
299 static inline unsigned int stack_size(struct stack
*stack
)
301 BT_ASSERT_DBG(stack
);
305 static void stack_pop(struct stack
*stack
)
307 struct bt_bfcr
*bfcr
;
309 BT_ASSERT_DBG(stack
);
310 BT_ASSERT_DBG(stack_size(stack
));
312 BT_CPPLOGT_SPEC(bfcr
->logger
,
313 "Popping from stack: "
314 "stack-addr={}, stack-size-before={}, stack-size-after={}",
315 fmt::ptr(stack
), stack
->entries
->len
, stack
->entries
->len
- 1);
319 static inline bool stack_empty(struct stack
*stack
)
321 return stack_size(stack
) == 0;
324 static void stack_clear(struct stack
*stack
)
326 BT_ASSERT_DBG(stack
);
330 static inline struct stack_entry
*stack_top(struct stack
*stack
)
332 BT_ASSERT_DBG(stack
);
333 BT_ASSERT_DBG(stack_size(stack
));
334 return &bt_g_array_index(stack
->entries
, struct stack_entry
, stack
->size
- 1);
337 static inline size_t available_bits(struct bt_bfcr
*bfcr
)
339 return bfcr
->buf
.sz
- bfcr
->buf
.at
;
342 static inline void consume_bits(struct bt_bfcr
*bfcr
, size_t incr
)
344 BT_CPPLOGT_SPEC(bfcr
->logger
, "Advancing cursor: bfcr-addr={}, cur-before={}, cur-after={}",
345 fmt::ptr(bfcr
), bfcr
->buf
.at
, bfcr
->buf
.at
+ incr
);
346 bfcr
->buf
.at
+= incr
;
349 static inline bool has_enough_bits(struct bt_bfcr
*bfcr
, size_t sz
)
351 return available_bits(bfcr
) >= sz
;
354 static inline bool at_least_one_bit_left(struct bt_bfcr
*bfcr
)
356 return has_enough_bits(bfcr
, 1);
359 static inline size_t packet_at(struct bt_bfcr
*bfcr
)
361 return bfcr
->buf
.packet_offset
+ bfcr
->buf
.at
;
364 static inline size_t buf_at_from_addr(struct bt_bfcr
*bfcr
)
369 * ====== offset ===== (17)
371 * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
373 * addr (0) ==== at ==== (12)
377 * =============================== (29)
379 return bfcr
->buf
.offset
+ bfcr
->buf
.at
;
382 static void stitch_reset(struct bt_bfcr
*bfcr
)
384 bfcr
->stitch
.offset
= 0;
388 static inline size_t stitch_at_from_addr(struct bt_bfcr
*bfcr
)
390 return bfcr
->stitch
.offset
+ bfcr
->stitch
.at
;
393 static void stitch_append_from_buf(struct bt_bfcr
*bfcr
, size_t sz
)
395 size_t stitch_byte_at
;
403 stitch_byte_at
= BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr
));
404 buf_byte_at
= BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr
));
405 nb_bytes
= BITS_TO_BYTES_CEIL(sz
);
406 BT_ASSERT(nb_bytes
> 0);
407 BT_ASSERT(bfcr
->buf
.addr
);
408 memcpy(&bfcr
->stitch
.buf
[stitch_byte_at
], &bfcr
->buf
.addr
[buf_byte_at
], nb_bytes
);
409 bfcr
->stitch
.at
+= sz
;
410 consume_bits(bfcr
, sz
);
413 static void stitch_append_from_remaining_buf(struct bt_bfcr
*bfcr
)
415 stitch_append_from_buf(bfcr
, available_bits(bfcr
));
418 static void stitch_set_from_remaining_buf(struct bt_bfcr
*bfcr
)
421 bfcr
->stitch
.offset
= IN_BYTE_OFFSET(buf_at_from_addr(bfcr
));
422 stitch_append_from_remaining_buf(bfcr
);
425 static inline void read_unsigned_bitfield(struct bt_bfcr
*bfcr
, const uint8_t *buf
, size_t at
,
426 unsigned int field_size
, enum ctf_byte_order bo
,
430 case CTF_BYTE_ORDER_BIG
:
431 bt_bitfield_read_be(buf
, uint8_t, at
, field_size
, v
);
433 case CTF_BYTE_ORDER_LITTLE
:
434 bt_bitfield_read_le(buf
, uint8_t, at
, field_size
, v
);
440 BT_CPPLOGT_SPEC(bfcr
->logger
, "Read unsigned bit array: cur={}, size={}, bo={}, val={}", at
,
441 field_size
, (int) bo
, *v
);
444 static inline void read_signed_bitfield(struct bt_bfcr
*bfcr
, const uint8_t *buf
, size_t at
,
445 unsigned int field_size
, enum ctf_byte_order bo
, int64_t *v
)
448 case CTF_BYTE_ORDER_BIG
:
449 bt_bitfield_read_be(buf
, uint8_t, at
, field_size
, v
);
451 case CTF_BYTE_ORDER_LITTLE
:
452 bt_bitfield_read_le(buf
, uint8_t, at
, field_size
, v
);
458 BT_CPPLOGT_SPEC(bfcr
->logger
, "Read signed bit array: cur={}, size={}, bo={}, val={}", at
,
459 field_size
, (int) bo
, *v
);
462 typedef enum bt_bfcr_status (*read_basic_and_call_cb_t
)(struct bt_bfcr
*, const uint8_t *, size_t);
464 static inline enum bt_bfcr_status
validate_contiguous_bo(struct bt_bfcr
*bfcr
,
465 enum ctf_byte_order next_bo
)
467 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
469 /* Always valid when at a byte boundary */
470 if (packet_at(bfcr
) % 8 == 0) {
474 /* Always valid if last byte order is unknown */
475 if (bfcr
->last_bo
== CTF_BYTE_ORDER_UNKNOWN
) {
479 /* Always valid if next byte order is unknown */
480 if (next_bo
== CTF_BYTE_ORDER_UNKNOWN
) {
484 /* Make sure last byte order is compatible with the next byte order */
485 switch (bfcr
->last_bo
) {
486 case CTF_BYTE_ORDER_BIG
:
487 if (next_bo
!= CTF_BYTE_ORDER_BIG
) {
488 status
= BT_BFCR_STATUS_ERROR
;
491 case CTF_BYTE_ORDER_LITTLE
:
492 if (next_bo
!= CTF_BYTE_ORDER_LITTLE
) {
493 status
= BT_BFCR_STATUS_ERROR
;
497 status
= BT_BFCR_STATUS_ERROR
;
502 BT_CPPLOGW_SPEC(bfcr
->logger
,
503 "Cannot read bit array: two different byte orders not at a byte boundary: "
504 "bfcr-addr={}, last-bo={}, next-bo={}",
505 fmt::ptr(bfcr
), (int) bfcr
->last_bo
, (int) next_bo
);
511 static enum bt_bfcr_status
read_basic_float_and_call_cb(struct bt_bfcr
*bfcr
, const uint8_t *buf
,
515 unsigned int field_size
;
516 enum ctf_byte_order bo
;
517 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
518 ctf_field_class_float
*fc
= ctf_field_class_as_float(bfcr
->cur_basic_field_class
);
521 field_size
= fc
->base
.size
;
522 bo
= fc
->base
.byte_order
;
525 switch (field_size
) {
535 read_unsigned_bitfield(bfcr
, buf
, at
, field_size
, bo
, &v
);
536 f32
.u
= (uint32_t) v
;
537 dblval
= (double) f32
.f
;
548 read_unsigned_bitfield(bfcr
, buf
, at
, field_size
, bo
, &f64
.u
);
553 /* Only 32-bit and 64-bit fields are supported currently */
557 BT_CPPLOGT_SPEC(bfcr
->logger
, "Read floating point number value: bfcr={}, cur={}, val={}",
558 fmt::ptr(bfcr
), at
, dblval
);
560 if (bfcr
->user
.cbs
.classes
.floating_point
) {
561 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (floating point number).");
562 status
= bfcr
->user
.cbs
.classes
.floating_point(dblval
, bfcr
->cur_basic_field_class
,
564 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
565 if (status
!= BT_BFCR_STATUS_OK
) {
566 BT_CPPLOGW_SPEC(bfcr
->logger
, "User function failed: bfcr-addr={}, status={}",
567 fmt::ptr(bfcr
), status
);
574 static inline enum bt_bfcr_status
read_basic_int_and_call_cb(struct bt_bfcr
*bfcr
,
575 const uint8_t *buf
, size_t at
)
577 unsigned int field_size
;
578 enum ctf_byte_order bo
;
579 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
580 ctf_field_class_int
*fc
= ctf_field_class_as_int(bfcr
->cur_basic_field_class
);
582 field_size
= fc
->base
.size
;
583 bo
= fc
->base
.byte_order
;
586 * Update current byte order now because we could be reading
587 * the integer value of an enumeration class, and thus we know
588 * here the actual supporting integer class's byte order.
595 read_signed_bitfield(bfcr
, buf
, at
, field_size
, bo
, &v
);
597 if (bfcr
->user
.cbs
.classes
.signed_int
) {
598 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (signed integer).");
600 bfcr
->user
.cbs
.classes
.signed_int(v
, bfcr
->cur_basic_field_class
, bfcr
->user
.data
);
601 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
602 if (status
!= BT_BFCR_STATUS_OK
) {
603 BT_CPPLOGW_SPEC(bfcr
->logger
,
604 "User function failed: "
605 "bfcr-addr={}, status={}",
606 fmt::ptr(bfcr
), status
);
612 read_unsigned_bitfield(bfcr
, buf
, at
, field_size
, bo
, &v
);
614 if (bfcr
->user
.cbs
.classes
.unsigned_int
) {
615 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (unsigned integer).");
616 status
= bfcr
->user
.cbs
.classes
.unsigned_int(v
, bfcr
->cur_basic_field_class
,
618 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
619 if (status
!= BT_BFCR_STATUS_OK
) {
620 BT_CPPLOGW_SPEC(bfcr
->logger
,
621 "User function failed: "
622 "bfcr-addr={}, status={}",
623 fmt::ptr(bfcr
), status
);
631 static inline enum bt_bfcr_status
632 read_bit_array_class_and_call_continue(struct bt_bfcr
*bfcr
,
633 read_basic_and_call_cb_t read_basic_and_call_cb
)
637 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
638 ctf_field_class_bit_array
*fc
= ctf_field_class_as_bit_array(bfcr
->cur_basic_field_class
);
640 if (!at_least_one_bit_left(bfcr
)) {
641 BT_CPPLOGT_SPEC(bfcr
->logger
, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr
));
642 status
= BT_BFCR_STATUS_EOF
;
646 available
= available_bits(bfcr
);
647 needed_bits
= fc
->size
- bfcr
->stitch
.at
;
648 BT_CPPLOGT_SPEC(bfcr
->logger
,
649 "Continuing basic field decoding: "
650 "bfcr-addr={}, field-size={}, needed-size={}, "
652 fmt::ptr(bfcr
), fc
->size
, needed_bits
, available
);
653 if (needed_bits
<= available
) {
654 /* We have all the bits; append to stitch, then decode */
655 stitch_append_from_buf(bfcr
, needed_bits
);
656 status
= read_basic_and_call_cb(bfcr
, bfcr
->stitch
.buf
, bfcr
->stitch
.offset
);
657 if (status
!= BT_BFCR_STATUS_OK
) {
658 BT_CPPLOGW_SPEC(bfcr
->logger
,
659 "Cannot read basic field: "
660 "bfcr-addr={}, fc-addr={}, status={}",
661 fmt::ptr(bfcr
), fmt::ptr(bfcr
->cur_basic_field_class
), status
);
665 if (stack_empty(bfcr
->stack
)) {
666 /* Root is a basic class */
667 bfcr
->state
= BFCR_STATE_DONE
;
669 /* Go to next field */
670 stack_top(bfcr
->stack
)->index
++;
671 bfcr
->state
= BFCR_STATE_NEXT_FIELD
;
672 bfcr
->last_bo
= bfcr
->cur_bo
;
677 /* We are here; it means we don't have enough data to decode this */
679 bfcr
->logger
, "Not enough data to read the next basic field: appending to stitch buffer.");
680 stitch_append_from_remaining_buf(bfcr
);
681 status
= BT_BFCR_STATUS_EOF
;
687 static inline enum bt_bfcr_status
688 read_bit_array_class_and_call_begin(struct bt_bfcr
*bfcr
,
689 read_basic_and_call_cb_t read_basic_and_call_cb
)
692 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
693 ctf_field_class_bit_array
*fc
= ctf_field_class_as_bit_array(bfcr
->cur_basic_field_class
);
695 if (!at_least_one_bit_left(bfcr
)) {
696 BT_CPPLOGT_SPEC(bfcr
->logger
, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr
));
697 status
= BT_BFCR_STATUS_EOF
;
701 status
= validate_contiguous_bo(bfcr
, fc
->byte_order
);
702 if (status
!= BT_BFCR_STATUS_OK
) {
703 /* validate_contiguous_bo() logs errors */
707 available
= available_bits(bfcr
);
709 if (fc
->size
<= available
) {
710 /* We have all the bits; decode and set now */
711 BT_ASSERT_DBG(bfcr
->buf
.addr
);
712 status
= read_basic_and_call_cb(bfcr
, bfcr
->buf
.addr
, buf_at_from_addr(bfcr
));
713 if (status
!= BT_BFCR_STATUS_OK
) {
714 BT_CPPLOGW_SPEC(bfcr
->logger
,
715 "Cannot read basic field: "
716 "bfcr-addr={}, fc-addr={}, status={}",
717 fmt::ptr(bfcr
), fmt::ptr(bfcr
->cur_basic_field_class
), status
);
721 consume_bits(bfcr
, fc
->size
);
723 if (stack_empty(bfcr
->stack
)) {
724 /* Root is a basic class */
725 bfcr
->state
= BFCR_STATE_DONE
;
727 /* Go to next field */
728 stack_top(bfcr
->stack
)->index
++;
729 bfcr
->state
= BFCR_STATE_NEXT_FIELD
;
730 bfcr
->last_bo
= bfcr
->cur_bo
;
736 /* We are here; it means we don't have enough data to decode this */
737 BT_CPPLOGT_STR_SPEC(bfcr
->logger
,
738 "Not enough data to read the next basic field: setting stitch buffer.");
739 stitch_set_from_remaining_buf(bfcr
);
740 bfcr
->state
= BFCR_STATE_READ_BASIC_CONTINUE
;
741 status
= BT_BFCR_STATUS_EOF
;
747 static inline enum bt_bfcr_status
read_basic_int_class_and_call_begin(struct bt_bfcr
*bfcr
)
749 return read_bit_array_class_and_call_begin(bfcr
, read_basic_int_and_call_cb
);
752 static inline enum bt_bfcr_status
read_basic_int_class_and_call_continue(struct bt_bfcr
*bfcr
)
754 return read_bit_array_class_and_call_continue(bfcr
, read_basic_int_and_call_cb
);
757 static inline enum bt_bfcr_status
read_basic_float_class_and_call_begin(struct bt_bfcr
*bfcr
)
759 return read_bit_array_class_and_call_begin(bfcr
, read_basic_float_and_call_cb
);
762 static inline enum bt_bfcr_status
read_basic_float_class_and_call_continue(struct bt_bfcr
*bfcr
)
764 return read_bit_array_class_and_call_continue(bfcr
, read_basic_float_and_call_cb
);
767 static inline enum bt_bfcr_status
read_basic_string_class_and_call(struct bt_bfcr
*bfcr
, bool begin
)
770 const uint8_t *result
;
771 size_t available_bytes
;
772 const uint8_t *first_chr
;
773 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
775 if (!at_least_one_bit_left(bfcr
)) {
776 BT_CPPLOGT_SPEC(bfcr
->logger
, "Reached end of data: bfcr-addr={}", fmt::ptr(bfcr
));
777 status
= BT_BFCR_STATUS_EOF
;
781 BT_ASSERT_DBG(buf_at_from_addr(bfcr
) % 8 == 0);
782 available_bytes
= BITS_TO_BYTES_FLOOR(available_bits(bfcr
));
783 buf_at_bytes
= BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr
));
784 BT_ASSERT_DBG(bfcr
->buf
.addr
);
785 first_chr
= &bfcr
->buf
.addr
[buf_at_bytes
];
786 result
= (const uint8_t *) memchr(first_chr
, '\0', available_bytes
);
788 if (begin
&& bfcr
->user
.cbs
.classes
.string_begin
) {
789 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (string, beginning).");
790 status
= bfcr
->user
.cbs
.classes
.string_begin(bfcr
->cur_basic_field_class
, bfcr
->user
.data
);
791 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
792 if (status
!= BT_BFCR_STATUS_OK
) {
793 BT_CPPLOGW_SPEC(bfcr
->logger
, "User function failed: bfcr-addr={}, status={}",
794 fmt::ptr(bfcr
), status
);
800 /* No null character yet */
801 if (bfcr
->user
.cbs
.classes
.string
) {
802 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (substring).");
803 status
= bfcr
->user
.cbs
.classes
.string((const char *) first_chr
, available_bytes
,
804 bfcr
->cur_basic_field_class
, bfcr
->user
.data
);
805 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
806 if (status
!= BT_BFCR_STATUS_OK
) {
807 BT_CPPLOGW_SPEC(bfcr
->logger
,
808 "User function failed: "
809 "bfcr-addr={}, status={}",
810 fmt::ptr(bfcr
), status
);
815 consume_bits(bfcr
, BYTES_TO_BITS(available_bytes
));
816 bfcr
->state
= BFCR_STATE_READ_BASIC_CONTINUE
;
817 status
= BT_BFCR_STATUS_EOF
;
819 /* Found the null character */
820 size_t result_len
= (size_t) (result
- first_chr
);
822 if (bfcr
->user
.cbs
.classes
.string
&& result_len
) {
823 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (substring).");
824 status
= bfcr
->user
.cbs
.classes
.string((const char *) first_chr
, result_len
,
825 bfcr
->cur_basic_field_class
, bfcr
->user
.data
);
826 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
827 if (status
!= BT_BFCR_STATUS_OK
) {
828 BT_CPPLOGW_SPEC(bfcr
->logger
,
829 "User function failed: "
830 "bfcr-addr={}, status={}",
831 fmt::ptr(bfcr
), status
);
836 if (bfcr
->user
.cbs
.classes
.string_end
) {
837 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (string, end).");
839 bfcr
->user
.cbs
.classes
.string_end(bfcr
->cur_basic_field_class
, bfcr
->user
.data
);
840 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
841 if (status
!= BT_BFCR_STATUS_OK
) {
842 BT_CPPLOGW_SPEC(bfcr
->logger
,
843 "User function failed: "
844 "bfcr-addr={}, status={}",
845 fmt::ptr(bfcr
), status
);
850 consume_bits(bfcr
, BYTES_TO_BITS(result_len
+ 1));
852 if (stack_empty(bfcr
->stack
)) {
853 /* Root is a basic class */
854 bfcr
->state
= BFCR_STATE_DONE
;
856 /* Go to next field */
857 stack_top(bfcr
->stack
)->index
++;
858 bfcr
->state
= BFCR_STATE_NEXT_FIELD
;
859 bfcr
->last_bo
= bfcr
->cur_bo
;
867 static inline enum bt_bfcr_status
read_basic_begin_state(struct bt_bfcr
*bfcr
)
869 enum bt_bfcr_status status
;
871 BT_ASSERT_DBG(bfcr
->cur_basic_field_class
);
873 switch (bfcr
->cur_basic_field_class
->type
) {
874 case CTF_FIELD_CLASS_TYPE_INT
:
875 case CTF_FIELD_CLASS_TYPE_ENUM
:
876 status
= read_basic_int_class_and_call_begin(bfcr
);
878 case CTF_FIELD_CLASS_TYPE_FLOAT
:
879 status
= read_basic_float_class_and_call_begin(bfcr
);
881 case CTF_FIELD_CLASS_TYPE_STRING
:
882 status
= read_basic_string_class_and_call(bfcr
, true);
891 static inline enum bt_bfcr_status
read_basic_continue_state(struct bt_bfcr
*bfcr
)
893 enum bt_bfcr_status status
;
895 BT_ASSERT_DBG(bfcr
->cur_basic_field_class
);
897 switch (bfcr
->cur_basic_field_class
->type
) {
898 case CTF_FIELD_CLASS_TYPE_INT
:
899 case CTF_FIELD_CLASS_TYPE_ENUM
:
900 status
= read_basic_int_class_and_call_continue(bfcr
);
902 case CTF_FIELD_CLASS_TYPE_FLOAT
:
903 status
= read_basic_float_class_and_call_continue(bfcr
);
905 case CTF_FIELD_CLASS_TYPE_STRING
:
906 status
= read_basic_string_class_and_call(bfcr
, false);
915 static inline size_t bits_to_skip_to_align_to(struct bt_bfcr
*bfcr
, size_t align
)
917 size_t aligned_packet_at
;
919 aligned_packet_at
= BT_ALIGN(packet_at(bfcr
), align
);
920 return aligned_packet_at
- packet_at(bfcr
);
923 static inline enum bt_bfcr_status
align_class_state(struct bt_bfcr
*bfcr
,
924 struct ctf_field_class
*field_class
,
925 enum bfcr_state next_state
)
927 unsigned int field_alignment
;
929 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
931 /* Get field's alignment */
932 field_alignment
= field_class
->alignment
;
935 * 0 means "undefined" for variants; what we really want is 1
938 BT_ASSERT_DBG(field_alignment
>= 1);
940 /* Compute how many bits we need to skip */
941 skip_bits
= bits_to_skip_to_align_to(bfcr
, (size_t) field_alignment
);
943 /* Nothing to skip? aligned */
944 if (skip_bits
== 0) {
945 bfcr
->state
= next_state
;
949 /* Make sure there's at least one bit left */
950 if (!at_least_one_bit_left(bfcr
)) {
951 status
= BT_BFCR_STATUS_EOF
;
955 /* Consume as many bits as possible in what's left */
956 consume_bits(bfcr
, MIN(available_bits(bfcr
), skip_bits
));
958 /* Are we done now? */
959 skip_bits
= bits_to_skip_to_align_to(bfcr
, field_alignment
);
960 if (skip_bits
== 0) {
961 /* Yes: go to next state */
962 bfcr
->state
= next_state
;
965 /* No: need more data */
966 BT_CPPLOGT_SPEC(bfcr
->logger
, "Reached end of data when aligning: bfcr-addr={}",
968 status
= BT_BFCR_STATUS_EOF
;
975 static inline enum bt_bfcr_status
next_field_state(struct bt_bfcr
*bfcr
)
978 struct stack_entry
*top
;
979 struct ctf_field_class
*next_field_class
= NULL
;
980 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
982 if (stack_empty(bfcr
->stack
)) {
986 top
= stack_top(bfcr
->stack
);
988 /* Are we done with this base class? */
989 while (top
->index
== top
->base_len
) {
990 if (bfcr
->user
.cbs
.classes
.compound_end
) {
991 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (compound, end).");
992 status
= bfcr
->user
.cbs
.classes
.compound_end(top
->base_class
, bfcr
->user
.data
);
993 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
994 if (status
!= BT_BFCR_STATUS_OK
) {
995 BT_CPPLOGW_SPEC(bfcr
->logger
, "User function failed: bfcr-addr={}, status={}",
996 fmt::ptr(bfcr
), status
);
1001 stack_pop(bfcr
->stack
);
1003 /* Are we done with the root class? */
1004 if (stack_empty(bfcr
->stack
)) {
1005 bfcr
->state
= BFCR_STATE_DONE
;
1009 top
= stack_top(bfcr
->stack
);
1013 /* Get next field's class */
1014 switch (top
->base_class
->type
) {
1015 case CTF_FIELD_CLASS_TYPE_STRUCT
:
1016 next_field_class
= ctf_field_class_struct_borrow_member_by_index(
1017 ctf_field_class_as_struct(top
->base_class
), (uint64_t) top
->index
)
1020 case CTF_FIELD_CLASS_TYPE_ARRAY
:
1021 case CTF_FIELD_CLASS_TYPE_SEQUENCE
:
1023 ctf_field_class_array_base
*array_fc
= ctf_field_class_as_array_base(top
->base_class
);
1025 next_field_class
= array_fc
->elem_fc
;
1028 case CTF_FIELD_CLASS_TYPE_VARIANT
:
1029 /* Variant classes are dynamic: the user should know! */
1030 next_field_class
= bfcr
->user
.cbs
.query
.borrow_variant_selected_field_class(
1031 top
->base_class
, bfcr
->user
.data
);
1037 if (!next_field_class
) {
1038 BT_CPPLOGW_SPEC(bfcr
->logger
,
1039 "Cannot get the field class of the next field: "
1040 "bfcr-addr={}, base-fc-addr={}, base-fc-type={}, index={}",
1041 fmt::ptr(bfcr
), fmt::ptr(top
->base_class
), (int) top
->base_class
->type
,
1043 status
= BT_BFCR_STATUS_ERROR
;
1047 if (next_field_class
->is_compound
) {
1048 if (bfcr
->user
.cbs
.classes
.compound_begin
) {
1049 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (compound, begin).");
1050 status
= bfcr
->user
.cbs
.classes
.compound_begin(next_field_class
, bfcr
->user
.data
);
1051 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", status
);
1052 if (status
!= BT_BFCR_STATUS_OK
) {
1053 BT_CPPLOGW_SPEC(bfcr
->logger
, "User function failed: bfcr-addr={}, status={}",
1054 fmt::ptr(bfcr
), status
);
1059 ret
= stack_push_with_len(bfcr
, next_field_class
);
1061 /* stack_push_with_len() logs errors */
1062 status
= BT_BFCR_STATUS_ERROR
;
1066 /* Next state: align a compound class */
1067 bfcr
->state
= BFCR_STATE_ALIGN_COMPOUND
;
1069 /* Replace current basic field class */
1070 BT_CPPLOGT_SPEC(bfcr
->logger
,
1071 "Replacing current basic field class: "
1072 "bfcr-addr={}, cur-basic-fc-addr={}, "
1073 "next-basic-fc-addr={}",
1074 fmt::ptr(bfcr
), fmt::ptr(bfcr
->cur_basic_field_class
),
1075 fmt::ptr(next_field_class
));
1076 bfcr
->cur_basic_field_class
= next_field_class
;
1078 /* Next state: align a basic class */
1079 bfcr
->state
= BFCR_STATE_ALIGN_BASIC
;
1086 static inline enum bt_bfcr_status
handle_state(struct bt_bfcr
*bfcr
)
1088 enum bt_bfcr_status status
= BT_BFCR_STATUS_OK
;
1090 BT_CPPLOGT_SPEC(bfcr
->logger
, "Handling state: bfcr-addr={}, state={}", fmt::ptr(bfcr
),
1093 switch (bfcr
->state
) {
1094 case BFCR_STATE_NEXT_FIELD
:
1095 status
= next_field_state(bfcr
);
1097 case BFCR_STATE_ALIGN_BASIC
:
1098 status
= align_class_state(bfcr
, bfcr
->cur_basic_field_class
, BFCR_STATE_READ_BASIC_BEGIN
);
1100 case BFCR_STATE_ALIGN_COMPOUND
:
1101 status
= align_class_state(bfcr
, stack_top(bfcr
->stack
)->base_class
, BFCR_STATE_NEXT_FIELD
);
1103 case BFCR_STATE_READ_BASIC_BEGIN
:
1104 status
= read_basic_begin_state(bfcr
);
1106 case BFCR_STATE_READ_BASIC_CONTINUE
:
1107 status
= read_basic_continue_state(bfcr
);
1109 case BFCR_STATE_DONE
:
1113 BT_CPPLOGT_SPEC(bfcr
->logger
, "Handled state: bfcr-addr={}, status={}", fmt::ptr(bfcr
), status
);
1117 struct bt_bfcr
*bt_bfcr_create(struct bt_bfcr_cbs cbs
, void *data
, const bt2c::Logger
& logger
)
1119 BT_CPPLOGD_STR_SPEC(logger
, "Creating binary field class reader (BFCR).");
1121 bt_bfcr
*bfcr
= new bt_bfcr
{logger
};
1122 bfcr
->stack
= stack_new(bfcr
);
1124 BT_CPPLOGE_STR_SPEC(bfcr
->logger
, "Cannot create BFCR's stack.");
1125 bt_bfcr_destroy(bfcr
);
1130 bfcr
->state
= BFCR_STATE_NEXT_FIELD
;
1131 bfcr
->user
.cbs
= cbs
;
1132 bfcr
->user
.data
= data
;
1133 BT_CPPLOGD_SPEC(bfcr
->logger
, "Created BFCR: addr={}", fmt::ptr(bfcr
));
1139 void bt_bfcr_destroy(struct bt_bfcr
*bfcr
)
1142 stack_destroy(bfcr
->stack
);
1145 BT_CPPLOGD_SPEC(bfcr
->logger
, "Destroying BFCR: addr={}", fmt::ptr(bfcr
));
1149 static void reset(struct bt_bfcr
*bfcr
)
1151 BT_CPPLOGD_SPEC(bfcr
->logger
, "Resetting BFCR: addr={}", fmt::ptr(bfcr
));
1152 stack_clear(bfcr
->stack
);
1154 bfcr
->buf
.addr
= NULL
;
1155 bfcr
->last_bo
= CTF_BYTE_ORDER_UNKNOWN
;
1158 static void update_packet_offset(struct bt_bfcr
*bfcr
)
1160 BT_CPPLOGT_SPEC(bfcr
->logger
,
1161 "Updating packet offset for next call: "
1162 "bfcr-addr={}, cur-packet-offset={}, next-packet-offset={}",
1163 fmt::ptr(bfcr
), bfcr
->buf
.packet_offset
,
1164 bfcr
->buf
.packet_offset
+ bfcr
->buf
.at
);
1165 bfcr
->buf
.packet_offset
+= bfcr
->buf
.at
;
1168 size_t bt_bfcr_start(struct bt_bfcr
*bfcr
, struct ctf_field_class
*cls
, const uint8_t *buf
,
1169 size_t offset
, size_t packet_offset
, size_t sz
, enum bt_bfcr_status
*status
)
1171 BT_ASSERT_DBG(bfcr
);
1172 BT_ASSERT_DBG(BYTES_TO_BITS(sz
) >= offset
);
1174 bfcr
->buf
.addr
= buf
;
1175 bfcr
->buf
.offset
= offset
;
1177 bfcr
->buf
.packet_offset
= packet_offset
;
1178 bfcr
->buf
.buf_sz
= sz
;
1179 bfcr
->buf
.sz
= BYTES_TO_BITS(sz
) - offset
;
1180 *status
= BT_BFCR_STATUS_OK
;
1182 BT_CPPLOGT_SPEC(bfcr
->logger
,
1183 "Starting decoding: bfcr-addr={}, fc-addr={}, "
1184 "buf-addr={}, buf-size={}, offset={}, "
1186 fmt::ptr(bfcr
), fmt::ptr(cls
), fmt::ptr(buf
), sz
, offset
, packet_offset
);
1188 /* Set root class */
1189 if (cls
->is_compound
) {
1190 /* Compound class: push on visit stack */
1193 if (bfcr
->user
.cbs
.classes
.compound_begin
) {
1194 BT_CPPLOGT_SPEC(bfcr
->logger
, "Calling user function (compound, begin).");
1195 *status
= bfcr
->user
.cbs
.classes
.compound_begin(cls
, bfcr
->user
.data
);
1196 BT_CPPLOGT_SPEC(bfcr
->logger
, "User function returned: status={}", *status
);
1197 if (*status
!= BT_BFCR_STATUS_OK
) {
1198 BT_CPPLOGW_SPEC(bfcr
->logger
, "User function failed: bfcr-addr={}, status={}",
1199 fmt::ptr(bfcr
), *status
);
1204 stack_ret
= stack_push_with_len(bfcr
, cls
);
1206 /* stack_push_with_len() logs errors */
1207 *status
= BT_BFCR_STATUS_ERROR
;
1211 bfcr
->state
= BFCR_STATE_ALIGN_COMPOUND
;
1213 /* Basic class: set as current basic class */
1214 bfcr
->cur_basic_field_class
= cls
;
1215 bfcr
->state
= BFCR_STATE_ALIGN_BASIC
;
1218 /* Run the machine! */
1219 BT_CPPLOGT_STR_SPEC(bfcr
->logger
, "Running the state machine.");
1222 *status
= handle_state(bfcr
);
1223 if (*status
!= BT_BFCR_STATUS_OK
|| bfcr
->state
== BFCR_STATE_DONE
) {
1228 /* Update packet offset for next time */
1229 update_packet_offset(bfcr
);
1232 return bfcr
->buf
.at
;
1235 size_t bt_bfcr_continue(struct bt_bfcr
*bfcr
, const uint8_t *buf
, size_t sz
,
1236 enum bt_bfcr_status
*status
)
1238 BT_ASSERT_DBG(bfcr
);
1240 BT_ASSERT_DBG(sz
> 0);
1241 bfcr
->buf
.addr
= buf
;
1242 bfcr
->buf
.offset
= 0;
1244 bfcr
->buf
.buf_sz
= sz
;
1245 bfcr
->buf
.sz
= BYTES_TO_BITS(sz
);
1246 *status
= BT_BFCR_STATUS_OK
;
1248 BT_CPPLOGT_SPEC(bfcr
->logger
, "Continuing decoding: bfcr-addr={}, buf-addr={}, buf-size={}",
1249 fmt::ptr(bfcr
), fmt::ptr(buf
), sz
);
1251 /* Continue running the machine */
1252 BT_CPPLOGT_STR_SPEC(bfcr
->logger
, "Running the state machine.");
1255 *status
= handle_state(bfcr
);
1256 if (*status
!= BT_BFCR_STATUS_OK
|| bfcr
->state
== BFCR_STATE_DONE
) {
1261 /* Update packet offset for next time */
1262 update_packet_offset(bfcr
);
1263 return bfcr
->buf
.at
;
1266 void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr
*bfcr
, bt_bfcr_unsigned_int_cb_func cb
)
1268 BT_ASSERT_DBG(bfcr
);
1270 bfcr
->user
.cbs
.classes
.unsigned_int
= cb
;