`ctf` plugin: add `ctf::src::MsgIter` class
authorSimon Marchi <simon.marchi@efficios.com>
Tue, 7 Jun 2022 19:15:47 +0000 (15:15 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Tue, 23 Aug 2022 16:06:16 +0000 (12:06 -0400)
Original patch by Simon.

Philippe's changes over original patch (no functional change intended):

* Conform to the Babeltrace 2 coding style for new C++11 code and to
  some naming conventions of the common part of the `ctf` plugin.

* Add many public and internal comments.

* Rename `Quirks` class to `MsgIterQuirks`.

  Also, rename the quirks to the generic `pktEndDefClkValZero`,
  `eventRecordDefClkValGtNextPktBeginDefClkVal`, and
  `eventRecordDefClkValLtPktBeginDefClkVal` names to decouple this API
  from specific tracers.

* Remove `MsgIterItemVisitor`.

  Instead, MsgIter::_handleItem() dispatches the item to a specific
  handling method based on its numeric type.

  Move the relevant members of `MsgIterItemVisitor` into `MsgIter`
  itself.

* In `_StackFrame`, rename everything named "field" to "sub-field" so as
  to remove some ambiguity.

* Add MsgIter::_stackTop*() helper methods which defer to the
  corresponding method on the top stack frame.

* Add MsgIter::_stackPush() and MsgIter::_stackPop() helper methods.

* Add MsgIter::_addMsgToQueue() helper method.

* Only log item details when the `TRACE` level is enabled.

* Handle `PktMagicNumberItem`: validate the magic number value.

* Handle `MetadataStreamUuidItem`: validate the UUID.

  The `MsgIter` constructor now accepts an `expectedMetadataStreamUuid`
  parameter (optional UUID) which is the expected metadata stream UUID
  to compare to.

* Handle variable-length integer field items, adding the
  MsgIter::_handleUIntFieldItem() and MsgIter::_handleSIntFieldItem()
  helper method templates to do so.

* Handle BLOB field items.

  A new data member, `_mCurBlobFieldDataOffset`, tracks the current BLOB
  field data offset to write to when handling the next
  `BlobFieldSectionItem` (we can't append to a BLOB field like when
  handling `StrFieldSubstrItem`).

* Handle optional field items, adding a member to
  `MsgIter::_StackFrame::_Field`.

* In MsgIter::_StackFrame::goToNextSubField(), unconditionally increment
  `_mSubFieldIndex`: curSubField() doesn't care about `_mSubFieldIndex`
  for the variant/option field cases anyway.

  Therefore, make curSubFieldAndGoToNextSubField() reuse curSubField()
  and goToNextSubField() to remove some almost duplicate code.

Change-Id: If2dd82b43f4d19a16ec4420c13c9a3686034766d
Reviewed-on: https://review.lttng.org/c/babeltrace/+/8227
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
src/cpp-common/bt2/message.hpp
src/plugins/ctf/common/src/Makefile.am
src/plugins/ctf/common/src/msg-iter.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/msg-iter.hpp [new file with mode: 0644]

index da006a5f50ea30cfc7f50cbf7d88c21aee71f95e..8d54a8da552b469aacaf9d1f66d305c1594ad438 100644 (file)
@@ -17,6 +17,7 @@
 #include "internal/borrowed-obj.hpp"
 #include "internal/shared-obj.hpp"
 #include "internal/utils.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
 #include "cpp-common/optional.hpp"
 #include "cpp-common/string_view.hpp"
 #include "exc.hpp"
index 8ae05c18576761b6322b84fea93b0d46c9357e35..739fdc2731207e634bc3fadce9efe1542d311e38 100644 (file)
@@ -5,8 +5,11 @@ SUBDIRS = metadata bfcr msg-iter item-seq
 noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-common-src.la
 
 libbabeltrace2_plugin_ctf_common_src_la_SOURCES = \
-       pkt-props.hpp pkt-props.cpp \
-       clk-cls-cfg.hpp
+       clk-cls-cfg.hpp \
+       msg-iter.cpp \
+       msg-iter.hpp \
+       pkt-props.cpp \
+       pkt-props.hpp
 
 libbabeltrace2_plugin_ctf_common_src_la_LIBADD =       \
        $(builddir)/metadata/libctf-src-metadata.la     \
diff --git a/src/plugins/ctf/common/src/msg-iter.cpp b/src/plugins/ctf/common/src/msg-iter.cpp
new file mode 100644 (file)
index 0000000..95780fc
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 Simon Marchi <simon.marchi@efficios.com>
+ * Copyright (c) 2015-2022 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_CLOG_CFG _mLogCfg
+#define BT_LOG_TAG  "PLUGIN/CTF/MSG-ITER"
+#include "cpp-common/cfg-logging-error-reporting-throw.hpp"
+
+#include <algorithm>
+
+#include "cpp-common/data-len.hpp"
+#include "item-seq/item.hpp"
+#include "msg-iter.hpp"
+
+namespace ctf {
+namespace src {
+
+namespace bt2c = bt2_common;
+using namespace bt2c::literals::datalen;
+
+MsgIter::MsgIter(bt_self_message_iterator * const selfMsgIter, const ctf::src::TraceCls& traceCls,
+                 nonstd::optional<bt2_common::Uuid> expectedMetadataStreamUuid,
+                 const bt2::Stream stream, Medium::UP medium, const MsgIterQuirks& quirks,
+                 const bt2c::LogCfg& logCfg) :
+    _mSelfMsgIter {selfMsgIter},
+    _mStream {stream}, _mExpectedMetadataStreamUuid {std::move(expectedMetadataStreamUuid)},
+    _mQuirks {quirks}, _mItemSeqIter {std::move(medium), traceCls, logCfg}, _mLogCfg {logCfg},
+    _mLoggingVisitor {"Handling item", logCfg}
+{
+    BT_CLOGD("Created CTF plugin message iterator: "
+             "addr=%p, trace-cls-addr=%p, log-level=%s",
+             this, &traceCls, bt_common_logging_level_string(logCfg.logLevel()));
+}
+
+nonstd::optional<bt2::ConstMessage::Shared> MsgIter::next()
+{
+    BT_CLOGD("Getting next message: addr=%p", this);
+
+    if (_mIsDone) {
+        return nonstd::nullopt;
+    }
+
+    /*
+     * Return any message that's already in the queue (one iteration
+     * of the underlying item sequence iterator may yield more than
+     * one message, but we return one at a time).
+     */
+    if (auto msg = this->_releaseNextMsg()) {
+        return msg;
+    }
+
+    try {
+        while (true) {
+            /*
+             * Get the next item from the underlying item sequence
+             * iterator.
+             */
+            const auto item = _mItemSeqIter.next();
+
+            if (!item) {
+                /* No more items: this is the end! */
+                break;
+            }
+
+            /* Handle item if needed */
+            if (!_mSkipItemsUntilScopeEndItem || item->isScopeEndItem()) {
+                this->_handleItem(*item);
+
+                if (auto msg = this->_releaseNextMsg()) {
+                    return msg;
+                }
+            }
+        }
+
+        /* We're done! */
+        _mIsDone = true;
+        return bt2::Message::Shared::createWithoutRef(
+            bt_message_stream_end_create(_mSelfMsgIter, _mStream.libObjPtr()));
+    } catch (const bt2c::Error&) {
+        BT_CLOGE_APPEND_CAUSE_AND_RETHROW("Failed to create next message: addr=%p", this);
+    }
+}
+
+void MsgIter::_handleItem(const Item& item)
+{
+    /* Log item details */
+    if (BT_LOG_ON_TRACE) {
+        item.accept(_mLoggingVisitor);
+    }
+
+    /* Defer to specific handler */
+    switch (item.type()) {
+    case Item::Type::PKT_BEGIN:
+        this->_handleItem(static_cast<const PktBeginItem&>(item));
+        break;
+    case Item::Type::PKT_END:
+        this->_handleItem(static_cast<const PktEndItem&>(item));
+        break;
+    case Item::Type::SCOPE_BEGIN:
+        this->_handleItem(static_cast<const ScopeBeginItem&>(item));
+        break;
+    case Item::Type::SCOPE_END:
+        this->_handleItem(static_cast<const ScopeEndItem&>(item));
+        break;
+    case Item::Type::PKT_CONTENT_END:
+        this->_handleItem(static_cast<const PktContentEndItem&>(item));
+        break;
+    case Item::Type::EVENT_RECORD_END:
+        this->_handleItem(static_cast<const EventRecordEndItem&>(item));
+        break;
+    case Item::Type::PKT_MAGIC_NUMBER:
+        this->_handleItem(static_cast<const PktMagicNumberItem&>(item));
+        break;
+    case Item::Type::METADATA_STREAM_UUID:
+        this->_handleItem(static_cast<const MetadataStreamUuidItem&>(item));
+        break;
+    case Item::Type::DATA_STREAM_INFO:
+        this->_handleItem(static_cast<const DataStreamInfoItem&>(item));
+        break;
+    case Item::Type::PKT_INFO:
+        this->_handleItem(static_cast<const PktInfoItem&>(item));
+        break;
+    case Item::Type::EVENT_RECORD_INFO:
+        this->_handleItem(static_cast<const EventRecordInfoItem&>(item));
+        break;
+    case Item::Type::FIXED_LEN_BIT_ARRAY_FIELD:
+        this->_handleItem(static_cast<const FixedLenBitArrayFieldItem&>(item));
+        break;
+    case Item::Type::FIXED_LEN_BOOL_FIELD:
+        this->_handleItem(static_cast<const FixedLenBoolFieldItem&>(item));
+        break;
+    case Item::Type::FIXED_LEN_SINT_FIELD:
+    case Item::Type::FIXED_LEN_SENUM_FIELD:
+        this->_handleItem(static_cast<const FixedLenSIntFieldItem&>(item));
+        break;
+    case Item::Type::FIXED_LEN_UINT_FIELD:
+    case Item::Type::FIXED_LEN_UENUM_FIELD:
+        this->_handleItem(static_cast<const FixedLenUIntFieldItem&>(item));
+        break;
+    case Item::Type::FIXED_LEN_FLOAT_FIELD:
+        this->_handleItem(static_cast<const FixedLenFloatFieldItem&>(item));
+        break;
+    case Item::Type::VAR_LEN_SINT_FIELD:
+    case Item::Type::VAR_LEN_SENUM_FIELD:
+        this->_handleItem(static_cast<const VarLenSIntFieldItem&>(item));
+        break;
+    case Item::Type::VAR_LEN_UINT_FIELD:
+    case Item::Type::VAR_LEN_UENUM_FIELD:
+        this->_handleItem(static_cast<const VarLenUIntFieldItem&>(item));
+        break;
+    case Item::Type::NULL_TERMINATED_STR_FIELD_BEGIN:
+        this->_handleItem(static_cast<const NullTerminatedStrFieldBeginItem&>(item));
+        break;
+    case Item::Type::NULL_TERMINATED_STR_FIELD_END:
+        this->_handleItem(static_cast<const NullTerminatedStrFieldEndItem&>(item));
+        break;
+    case Item::Type::STR_FIELD_SUBSTR:
+        this->_handleItem(static_cast<const StrFieldSubstrItem&>(item));
+        break;
+    case Item::Type::BLOB_FIELD_SECTION:
+        this->_handleItem(static_cast<const BlobFieldSectionItem&>(item));
+        break;
+    case Item::Type::STRUCT_FIELD_BEGIN:
+        this->_handleItem(static_cast<const StructFieldBeginItem&>(item));
+        break;
+    case Item::Type::STRUCT_FIELD_END:
+        this->_handleItem(static_cast<const StructFieldEndItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_ARRAY_FIELD_BEGIN:
+        this->_handleItem(static_cast<const StaticLenArrayFieldBeginItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_ARRAY_FIELD_END:
+    case Item::Type::DYN_LEN_ARRAY_FIELD_END:
+        this->_handleItem(static_cast<const ArrayFieldEndItem&>(item));
+        break;
+    case Item::Type::DYN_LEN_ARRAY_FIELD_BEGIN:
+        this->_handleItem(static_cast<const DynLenArrayFieldBeginItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_BLOB_FIELD_BEGIN:
+        this->_handleItem(static_cast<const StaticLenBlobFieldBeginItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_BLOB_FIELD_END:
+    case Item::Type::DYN_LEN_BLOB_FIELD_END:
+        this->_handleItem(static_cast<const BlobFieldEndItem&>(item));
+        break;
+    case Item::Type::DYN_LEN_BLOB_FIELD_BEGIN:
+        this->_handleItem(static_cast<const DynLenBlobFieldBeginItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_STR_FIELD_BEGIN:
+    case Item::Type::DYN_LEN_STR_FIELD_BEGIN:
+        this->_handleItem(static_cast<const NonNullTerminatedStrFieldBeginItem&>(item));
+        break;
+    case Item::Type::STATIC_LEN_STR_FIELD_END:
+    case Item::Type::DYN_LEN_STR_FIELD_END:
+        this->_handleItem(static_cast<const NonNullTerminatedStrFieldEndItem&>(item));
+        break;
+    case Item::Type::VARIANT_FIELD_WITH_SINT_SEL_BEGIN:
+    case Item::Type::VARIANT_FIELD_WITH_UINT_SEL_BEGIN:
+        this->_handleItem(static_cast<const VariantFieldBeginItem&>(item));
+        break;
+    case Item::Type::VARIANT_FIELD_WITH_SINT_SEL_END:
+    case Item::Type::VARIANT_FIELD_WITH_UINT_SEL_END:
+        this->_handleItem(static_cast<const VariantFieldEndItem&>(item));
+        break;
+    case Item::Type::OPTIONAL_FIELD_WITH_BOOL_SEL_BEGIN:
+    case Item::Type::OPTIONAL_FIELD_WITH_SINT_SEL_BEGIN:
+    case Item::Type::OPTIONAL_FIELD_WITH_UINT_SEL_BEGIN:
+        this->_handleItem(static_cast<const OptionalFieldBeginItem&>(item));
+        break;
+    case Item::Type::OPTIONAL_FIELD_WITH_BOOL_SEL_END:
+    case Item::Type::OPTIONAL_FIELD_WITH_SINT_SEL_END:
+    case Item::Type::OPTIONAL_FIELD_WITH_UINT_SEL_END:
+        this->_handleItem(static_cast<const OptionalFieldEndItem&>(item));
+        break;
+    default:
+        BT_CLOGT_STR("Skipping item.");
+        return;
+    }
+}
+
+void MsgIter::_handleItem(const PktBeginItem&)
+{
+    BT_ASSERT_DBG(!this->_curPkt());
+    this->_curPkt(_mStream.createPacket());
+}
+
+bt_message *MsgIter::_createPktEndMsgAndUpdateCurDefClkVal()
+{
+    if (_mPktEndDefClkVal) {
+        const auto pktEndDefClkValZeroBug = _mQuirks.pktEndDefClkValZero && _mPktBeginDefClkVal &&
+                                            _mPktEndDefClkVal && *_mPktBeginDefClkVal != 0 &&
+                                            *_mPktEndDefClkVal == 0;
+        const auto eventRecordDefClkValGtNextPktBeginDefClkValBug =
+            _mQuirks.eventRecordDefClkValGtNextPktBeginDefClkVal && _mCurDefClkVal &&
+            _mPktEndDefClkVal && *_mPktEndDefClkVal < _mCurDefClkVal;
+        const auto anyBug =
+            pktEndDefClkValZeroBug || eventRecordDefClkValGtNextPktBeginDefClkValBug;
+        const auto defClkVal = anyBug ? *_mCurDefClkVal : *_mPktEndDefClkVal;
+
+        if (!anyBug) {
+            _mCurDefClkVal = _mPktEndDefClkVal;
+        }
+
+        return bt_message_packet_end_create_with_default_clock_snapshot(_mSelfMsgIter,
+                                                                        this->_curPkt(), defClkVal);
+    } else {
+        return bt_message_packet_end_create(_mSelfMsgIter, this->_curPkt());
+    }
+}
+
+void MsgIter::_handleItem(const PktEndItem&)
+{
+    BT_ASSERT_DBG(!_mCurMsg);
+    BT_ASSERT_DBG(this->_curPkt());
+
+    /* Emit a packet beginning message now if required to fix a quirk */
+    if (_mDelayPktBeginMsgEmission) {
+        this->_emitDelayedPktBeginMsg(_mPktEndDefClkVal);
+    }
+
+    /* Emit a packet end message */
+    this->_addMsgToQueue(this->_createPktEndMsgAndUpdateCurDefClkVal());
+
+    /* No more current packet */
+    this->_resetCurPkt();
+}
+
+void MsgIter::_handleItem(const ScopeBeginItem& item)
+{
+    BT_ASSERT(_mStack.empty());
+    BT_ASSERT(!_mCurScopeField);
+
+    /* Handle specific scope */
+    switch (item.scope()) {
+    case ir::FieldLocScope::PKT_HEADER:
+        /* Nothing needed from the packet header: fast-forward */
+        _mSkipItemsUntilScopeEndItem = true;
+        break;
+    case ir::FieldLocScope::PKT_CTX:
+    {
+        const auto pkt = this->_curPkt();
+
+        BT_ASSERT_DBG(pkt);
+
+        const auto pktCtxField = bt_packet_borrow_context_field(pkt);
+
+        if (pktCtxField) {
+            _mCurScopeField = bt2::StructureField {pktCtxField};
+        } else {
+            /* Nothing needed from the packet context: fast-forward */
+            _mSkipItemsUntilScopeEndItem = true;
+        }
+
+        break;
+    }
+    case ir::FieldLocScope::EVENT_RECORD_HEADER:
+        /* Nothing needed from the event record header: fast-forward */
+        _mSkipItemsUntilScopeEndItem = true;
+        break;
+    case ir::FieldLocScope::EVENT_RECORD_COMMON_CTX:
+    {
+        const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+        const auto commonCtxField = bt_event_borrow_common_context_field(event);
+
+        if (commonCtxField) {
+            _mCurScopeField = bt2::StructureField {commonCtxField};
+        } else {
+            /* Nothing needed from the common context: fast-forward */
+            _mSkipItemsUntilScopeEndItem = true;
+        }
+
+        break;
+    }
+    case ir::FieldLocScope::EVENT_RECORD_SPEC_CTX:
+    {
+        const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+        const auto specCtxField = bt_event_borrow_specific_context_field(event);
+
+        if (specCtxField) {
+            _mCurScopeField = bt2::StructureField {specCtxField};
+        } else {
+            /* Nothing needed from the specific context: fast-forward */
+            _mSkipItemsUntilScopeEndItem = true;
+        }
+
+        break;
+    }
+    case ir::FieldLocScope::EVENT_RECORD_PAYLOAD:
+    {
+        const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+        const auto payloadField = bt_event_borrow_payload_field(event);
+
+        if (payloadField) {
+            _mCurScopeField = bt2::StructureField {payloadField};
+        } else {
+            /* Nothing needed from the payload: fast-forward */
+            _mSkipItemsUntilScopeEndItem = true;
+        }
+
+        break;
+    }
+    default:
+        bt_common_abort();
+    }
+}
+
+void MsgIter::_handleItem(const ScopeEndItem&)
+{
+    /*
+     * The last stack frame was removed by the `StructFieldEndItem`
+     * handler.
+     */
+    BT_ASSERT_DBG(_mStack.empty());
+
+    /* No more current scope root field */
+    _mCurScopeField.reset();
+
+    /* Reset this flag */
+    _mSkipItemsUntilScopeEndItem = false;
+}
+
+void MsgIter::_handleItem(const PktContentEndItem&)
+{
+    BT_ASSERT_DBG(this->_curPkt());
+}
+
+void MsgIter::_handleItem(const EventRecordEndItem&)
+{
+    BT_ASSERT_DBG(_mStack.empty());
+    BT_ASSERT_DBG(_mCurMsg);
+
+    /* Emit current message */
+    _mMsgs.emplace(bt2::ConstMessage::Shared::createWithoutRef((_mCurMsg->release())));
+    _mCurMsg.reset();
+}
+
+void MsgIter::_handleItem(const PktMagicNumberItem& item)
+{
+    if (!item.isValid()) {
+        BT_CLOGE_APPEND_CAUSE_AND_THROW(
+            bt2c::Error, "Invalid packet magic number: val=%x, expected-val=%x",
+            static_cast<unsigned int>(item.val()), static_cast<unsigned int>(item.expectedVal()));
+    }
+}
+
+void MsgIter::_handleItem(const MetadataStreamUuidItem& item)
+{
+    BT_ASSERT_DBG(_mExpectedMetadataStreamUuid);
+
+    if (item.uuid() != *_mExpectedMetadataStreamUuid) {
+        BT_CLOGE_APPEND_CAUSE_AND_THROW(
+            bt2c::Error, "Invalid metadata stream UUID: uuid=%s, expected-uuid=%s",
+            item.uuid().str().c_str(), _mExpectedMetadataStreamUuid->str().c_str());
+    }
+}
+
+void MsgIter::_handleItem(const DataStreamInfoItem&)
+{
+    if (!_mEmittedStreamBeginMsg) {
+        this->_addMsgToQueue(
+            bt_message_stream_beginning_create(_mSelfMsgIter, _mStream.libObjPtr()));
+        _mEmittedStreamBeginMsg = true;
+    }
+}
+
+bt_message *MsgIter::_createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal)
+{
+    if (_mStream.cls().discardedEventsHaveDefaultClockSnapshots()) {
+        /*
+         * We know there was a previous packet since we can't reach this
+         * point for the first packet.
+         */
+        BT_ASSERT_DBG(prevPktEndDefClkVal);
+        return bt_message_discarded_events_create_with_default_clock_snapshots(
+            _mSelfMsgIter, _mStream.libObjPtr(), *prevPktEndDefClkVal, *_mPktEndDefClkVal);
+    } else {
+        return bt_message_discarded_events_create(_mSelfMsgIter, _mStream.libObjPtr());
+    }
+}
+
+bt_message *MsgIter::_createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal)
+{
+    if (_mStream.cls().discardedPacketsHaveDefaultClockSnapshots()) {
+        /*
+         * We know there was a previous packet since we can't reach this
+         * point for the first packet.
+         */
+        BT_ASSERT_DBG(prevPktEndDefClkVal);
+        return bt_message_discarded_packets_create_with_default_clock_snapshots(
+            _mSelfMsgIter, _mStream.libObjPtr(), *prevPktEndDefClkVal, *_mPktBeginDefClkVal);
+    } else {
+        return bt_message_discarded_packets_create(_mSelfMsgIter, _mStream.libObjPtr());
+    }
+}
+
+void MsgIter::_emitPktBeginMsg(const _OptUll& defClkVal)
+{
+    /* Create message */
+    const auto msg = [this, &defClkVal] {
+        if (defClkVal) {
+            _mCurDefClkVal = defClkVal;
+            return bt_message_packet_beginning_create_with_default_clock_snapshot(
+                _mSelfMsgIter, this->_curPkt(), *defClkVal);
+        } else {
+            return bt_message_packet_beginning_create(_mSelfMsgIter, this->_curPkt());
+        }
+    }();
+
+    /* Add to queue */
+    this->_addMsgToQueue(msg);
+}
+
+void MsgIter::_emitDelayedPktBeginMsg(const _OptUll& otherDefClkVal)
+{
+    BT_ASSERT_DBG(_mDelayPktBeginMsgEmission);
+
+    /* Reset the flag */
+    _mDelayPktBeginMsgEmission = false;
+
+    /*
+     * Only fix the beginning timestamp of the packet if it's larger
+     * than the timestamp of its first event record.
+     */
+    const auto defClkVal = [this, &otherDefClkVal]() -> _OptUll {
+        if (_mPktBeginDefClkVal && otherDefClkVal) {
+            return std::min(*_mPktBeginDefClkVal, *otherDefClkVal);
+        } else if (_mPktBeginDefClkVal) {
+            return _mPktBeginDefClkVal;
+        } else if (otherDefClkVal) {
+            return otherDefClkVal;
+        }
+
+        return nonstd::nullopt;
+    }();
+
+    /* Emit a packet beginning message now */
+    this->_emitPktBeginMsg(defClkVal);
+}
+
+void MsgIter::_handleItem(const PktInfoItem& item)
+{
+    /*
+     * Save the packet beginning and end timestamps.
+     *
+     * Also keep the end timestamp of the previous packet: we might need
+     * it if there are discarded event records.
+     */
+    const auto prevPktEndDefClkVal = _mPktEndDefClkVal;
+
+    _mPktBeginDefClkVal = item.beginDefClkVal();
+    _mPktEndDefClkVal = item.endDefClkVal();
+
+    /*
+     * Emit a discarded events message if the count of discarded event
+     * records went up since the previous packet.
+     *
+     * For the first packet, `_mCurDiscErCounterSnap` isn't set: we
+     * don't have anything to compare to.
+     */
+    const auto discErCounterSnap = item.discEventRecordCounterSnap();
+
+    if (_mCurDiscErCounterSnap) {
+        /*
+         * If the previous packet of this same stream had a discarded
+         * event record counter snapshot, then this one must have one
+         * too.
+         */
+        BT_ASSERT_DBG(discErCounterSnap);
+
+        if (*discErCounterSnap > *_mCurDiscErCounterSnap) {
+            /* Create and initialize the message */
+            const auto msg = this->_createInitDiscEventsMsg(prevPktEndDefClkVal);
+
+            /* Set its count */
+            bt_message_discarded_events_set_count(msg,
+                                                  *discErCounterSnap - *_mCurDiscErCounterSnap);
+
+            /* Add to queue */
+            this->_addMsgToQueue(msg);
+        }
+    }
+
+    /* Set new current discarded event record counter snapshot */
+    _mCurDiscErCounterSnap = discErCounterSnap;
+
+    /*
+     * Emit a discarded packets message if there's a gap between the
+     * previous packet sequence number and the sequence number of this
+     * new packet.
+     */
+    const auto seqNum = item.seqNum();
+
+    if (_mCurPktSeqNum) {
+        /*
+         * If the previous packet of this same stream had a sequence
+         * number, then this one must have one too.
+         */
+        BT_ASSERT_DBG(seqNum);
+
+        if (*_mCurPktSeqNum + 1 < *seqNum) {
+            /* Create and initialize the message */
+            const auto msg = this->_createInitDiscPktsMsg(prevPktEndDefClkVal);
+
+            /* Set its count */
+            bt_message_discarded_packets_set_count(msg, *seqNum - *_mCurPktSeqNum - 1);
+
+            /* Add to queue */
+            this->_addMsgToQueue(msg);
+        }
+    }
+
+    /* Set new packet sequence number */
+    _mCurPktSeqNum = seqNum;
+
+    /* There's no pending message */
+    BT_ASSERT_DBG(!_mCurMsg);
+
+    /*
+     * Depending on a quirk to handle, emit a packet beginning message
+     * now or delay said emission.
+     */
+    if (_mQuirks.eventRecordDefClkValLtPktBeginDefClkVal) {
+        _mDelayPktBeginMsgEmission = true;
+    } else {
+        /* No quirk to handle: emit the message now */
+        this->_emitPktBeginMsg(_mPktBeginDefClkVal);
+    }
+}
+
+bt_message *MsgIter::_createEventMsg(const bt2::EventClass cls, const _OptUll& defClkVal)
+{
+    if (defClkVal) {
+        if (this->_curPkt()) {
+            return bt_message_event_create_with_packet_and_default_clock_snapshot(
+                _mSelfMsgIter, cls.libObjPtr(), this->_curPkt(), *defClkVal);
+        } else {
+            return bt_message_event_create_with_default_clock_snapshot(
+                _mSelfMsgIter, cls.libObjPtr(), _mStream.libObjPtr(), *defClkVal);
+        }
+    } else {
+        if (this->_curPkt()) {
+            return bt_message_event_create_with_packet(_mSelfMsgIter, cls.libObjPtr(),
+                                                       this->_curPkt());
+        } else {
+            return bt_message_event_create(_mSelfMsgIter, cls.libObjPtr(), _mStream.libObjPtr());
+        }
+    }
+}
+
+void MsgIter::_handleItem(const EventRecordInfoItem& item)
+{
+    /* TODO: Test having a trace with only event record headers */
+    BT_ASSERT_DBG(item.cls());
+    BT_ASSERT_DBG(item.cls()->libCls());
+    BT_ASSERT_DBG(!_mCurMsg);
+
+    /* Emit a packet beginning message now if required to fix a quirk */
+    if (_mDelayPktBeginMsgEmission) {
+        this->_emitDelayedPktBeginMsg(item.defClkVal());
+    }
+
+    /* Update the default clock value if needed */
+    if (item.defClkVal()) {
+        _mCurDefClkVal = *item.defClkVal();
+    }
+
+    /*
+     * Set as current message.
+     *
+     * The following items will gradually fill this message.
+     *
+     * This message will be emitted (added to the message queue) when
+     * handling the next `EventRecordEndItem`.
+     */
+    _mCurMsg.emplace(bt2::Message::Shared::createWithoutRef(
+        this->_createEventMsg(*item.cls()->libCls(), item.defClkVal())));
+}
+
+void MsgIter::_handleItem(const FixedLenBitArrayFieldItem& item)
+{
+    if (_ignoreFieldItem(item)) {
+        return;
+    }
+
+    this->_stackTopCurSubFieldAndGoToNextSubField().asBitArray() = item.uIntVal();
+}
+
+void MsgIter::_handleItem(const FixedLenBoolFieldItem& item)
+{
+    if (_ignoreFieldItem(item)) {
+        return;
+    }
+
+    this->_stackTopCurSubFieldAndGoToNextSubField().asBool() = item.val();
+}
+
+void MsgIter::_handleItem(const FixedLenSIntFieldItem& item)
+{
+    this->_handleSIntFieldItem(item);
+}
+
+void MsgIter::_handleItem(const FixedLenUIntFieldItem& item)
+{
+    this->_handleUIntFieldItem(item);
+}
+
+void MsgIter::_handleItem(const FixedLenFloatFieldItem& item)
+{
+    const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+    if (item.cls().len() == 32_bits) {
+        field.asSinglePrecisionReal() = item.val();
+    } else {
+        BT_ASSERT_DBG(item.cls().len() == 64_bits);
+        field.asDoublePrecisionReal() = item.val();
+    }
+}
+
+void MsgIter::_handleItem(const VarLenSIntFieldItem& item)
+{
+    this->_handleSIntFieldItem(item);
+}
+
+void MsgIter::_handleItem(const VarLenUIntFieldItem& item)
+{
+    this->_handleUIntFieldItem(item);
+}
+
+void MsgIter::_handleStrFieldBeginItem()
+{
+    this->_stackTopCurSubField().asString() = "";
+    _mHaveStrFieldSubstrItemNullChar = false;
+}
+
+void MsgIter::_handleStrFieldEndItem()
+{
+    this->_stackTopGoToNextSubField();
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldBeginItem&)
+{
+    this->_handleStrFieldBeginItem();
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldEndItem&)
+{
+    this->_handleStrFieldEndItem();
+}
+
+void MsgIter::_handleItem(const StrFieldSubstrItem& item)
+{
+    if (_mHaveStrFieldSubstrItemNullChar) {
+        /* No more text data */
+        return;
+    }
+
+    const auto end = item.strEnd();
+
+    this->_stackTopCurSubField().asString().append(item.begin(), end - item.begin());
+    _mHaveStrFieldSubstrItemNullChar = end != item.end();
+}
+
+void MsgIter::_handleItem(const BlobFieldSectionItem& item)
+{
+    std::memcpy(&this->_stackTopCurSubField().asBlob().data()[_mCurBlobFieldDataOffset],
+                item.begin(), item.size().bytes());
+    _mCurBlobFieldDataOffset += item.size().bytes();
+}
+
+void MsgIter::_handleItem(const StructFieldBeginItem&)
+{
+    if (_mStack.empty()) {
+        /* This is the root field of the current scope */
+        BT_ASSERT_DBG(_mCurScopeField);
+        this->_stackPush(*_mCurScopeField);
+    } else {
+        /* Use sub-field */
+        this->_stackPush(this->_stackTopCurSubFieldAndGoToNextSubField().asStructure());
+    }
+}
+
+void MsgIter::_handleItem(const StructFieldEndItem&)
+{
+    this->_stackPop();
+}
+
+void MsgIter::_handleItem(const StaticLenArrayFieldBeginItem&)
+{
+    this->_stackPush(this->_stackTopCurSubFieldAndGoToNextSubField().asArray());
+}
+
+void MsgIter::_handleItem(const DynLenArrayFieldBeginItem& item)
+{
+    auto arrayField = this->_stackTopCurSubFieldAndGoToNextSubField().asDynamicArray();
+
+    arrayField.length(item.len());
+    this->_stackPush(arrayField);
+}
+
+void MsgIter::_handleItem(const ArrayFieldEndItem&)
+{
+    this->_stackPop();
+}
+
+void MsgIter::_handleItem(const StaticLenBlobFieldBeginItem& item)
+{
+    _mCurBlobFieldDataOffset = 0;
+}
+
+void MsgIter::_handleItem(const DynLenBlobFieldBeginItem& item)
+{
+    this->_stackTopCurSubField().asDynamicBlob().length(item.len().bytes());
+    _mCurBlobFieldDataOffset = 0;
+}
+
+void MsgIter::_handleItem(const BlobFieldEndItem&)
+{
+    this->_stackTopGoToNextSubField();
+}
+
+void MsgIter::_handleItem(const NonNullTerminatedStrFieldBeginItem&)
+{
+    this->_handleStrFieldBeginItem();
+}
+
+void MsgIter::_handleItem(const NonNullTerminatedStrFieldEndItem&)
+{
+    this->_handleStrFieldEndItem();
+}
+
+void MsgIter::_handleItem(const VariantFieldBeginItem& item)
+{
+    auto field = this->_stackTopCurSubFieldAndGoToNextSubField().asVariant();
+
+    field.selectOption(item.selectedOptIndex());
+    this->_stackPush(field);
+}
+
+void MsgIter::_handleItem(const VariantFieldEndItem&)
+{
+    this->_stackPop();
+}
+
+void MsgIter::_handleItem(const OptionalFieldBeginItem& item)
+{
+    auto field = this->_stackTopCurSubFieldAndGoToNextSubField().asOption();
+
+    field.hasField(item.isEnabled());
+    this->_stackPush(field);
+}
+
+void MsgIter::_handleItem(const OptionalFieldEndItem& item)
+{
+    this->_stackPop();
+}
+
+void MsgIter::_addMsgToQueue(bt_message * const msg)
+{
+    _mMsgs.emplace(bt2::ConstMessage::Shared::createWithoutRef(msg));
+}
+
+nonstd::optional<bt2::ConstMessage::Shared> MsgIter::_releaseNextMsg()
+{
+    if (_mMsgs.empty()) {
+        return nonstd::nullopt;
+    }
+
+    auto msg = std::move(_mMsgs.front());
+
+    _mMsgs.pop();
+    return msg;
+}
+
+} /* namespace src */
+} /* namespace ctf */
diff --git a/src/plugins/ctf/common/src/msg-iter.hpp b/src/plugins/ctf/common/src/msg-iter.hpp
new file mode 100644 (file)
index 0000000..1e51706
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 EfficiOS, Inc
+ */
+
+#ifndef CTF_COMMON_SRC_MSG_ITER_HPP
+#define CTF_COMMON_SRC_MSG_ITER_HPP
+
+#include <babeltrace2/babeltrace.h>
+#include <queue>
+#include <stack>
+
+#include "common/assert.h"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/uuid.hpp"
+#include "item-seq/item-seq-iter.hpp"
+#include "item-seq/item-visitor.hpp"
+#include "item-seq/logging-item-visitor.hpp"
+
+namespace ctf {
+namespace src {
+
+/*
+ * Various quirks that a CTF message iterator can work around.
+ */
+struct MsgIterQuirks final
+{
+    /* Packet end timestamps set to zero */
+    bool pktEndDefClkValZero = false;
+
+    /*
+     * Timestamp of last event record of a packet is greater than the
+     * beginning timestamp of the next packet.
+     */
+    bool eventRecordDefClkValGtNextPktBeginDefClkVal = false;
+
+    /*
+     * Timestamp of the first event record of a packet is less than the
+     * beginning timestamp of its packet.
+     */
+    bool eventRecordDefClkValLtPktBeginDefClkVal = false;
+};
+
+/*
+ * CTF message iterator.
+ *
+ * Such an iterator essentially converts the items of an underlying item
+ * sequence iterator to corresponding libbabeltrace2 messages.
+ *
+ * Therefore, as a user, you provide:
+ *
+ * • A medium, which provides binary stream data to the iterator.
+ *
+ * • A CTF IR trace class, which describes how to decode said data.
+ *
+ * • A libbabeltrace2 self message iterator and stream, which the
+ *   iterator needs to create libbabeltrace2 messages.
+ *
+ * A CTF message iterator may automatically fix some common quirks (see
+ * `MsgIterQuirks`).
+ */
+class MsgIter final
+{
+public:
+    /*
+     * Builds a CTF message iterator, using `traceCls` and `medium`
+     * to decode a data stream identified by `stream`, and `selfMsgIter`
+     * and `stream` to create libbabeltrace2 messages.
+     *
+     * `quirks` indicates which quirks to fix.
+     *
+     * It's guaranteed that this constructor doesn't throw
+     * `bt2_common::TryAgain` or a medium error.
+     */
+    explicit MsgIter(bt_self_message_iterator *selfMsgIter, const ctf::src::TraceCls& traceCls,
+                     nonstd::optional<bt2_common::Uuid> expectedMetadataStreamUuid,
+                     bt2::Stream stream, Medium::UP medium, const MsgIterQuirks& quirks,
+                     const bt2_common::LogCfg& logCfg);
+
+    /* Disable copy/move operations */
+    MsgIter(const MsgIter&) = delete;
+    MsgIter& operator=(const MsgIter&) = delete;
+
+    /*
+     * Advances the iterator to the next message, returning:
+     *
+     * A libbabeltrace2 message:
+     *     Said next message.
+     *
+     * `nonstd::nullopt`:
+     *     The iterator is ended.
+     *
+     * May throw whatever Medium::buf() may throw as well as
+     * `bt2_common::Error`.
+     */
+    nonstd::optional<bt2::ConstMessage::Shared> next();
+
+private:
+    /* An optional `unsigned long long` value */
+    using _OptUll = nonstd::optional<unsigned long long>;
+
+    /* Single frame of a message iterator stack */
+    class _StackFrame
+    {
+    public:
+        explicit _StackFrame(const bt2::StructureField field) noexcept :
+            _mFieldType {_FieldType::STRUCT}, _mField {field}
+        {
+        }
+
+        explicit _StackFrame(const bt2::VariantField field) noexcept :
+            _mFieldType {_FieldType::VARIANT}, _mField(field)
+        {
+        }
+
+        explicit _StackFrame(const bt2::OptionField field) noexcept :
+            _mFieldType {_FieldType::OPTION}, _mField(field)
+        {
+        }
+
+        explicit _StackFrame(const bt2::ArrayField field) noexcept :
+            _mFieldType(_FieldType::ARRAY), _mField(field)
+        {
+        }
+
+        bt2::StructureField structureField() const noexcept
+        {
+            BT_ASSERT_DBG(_mFieldType == _FieldType::STRUCT);
+            return _mField.structure;
+        }
+
+        bt2::VariantField variantField() const noexcept
+        {
+            BT_ASSERT_DBG(_mFieldType == _FieldType::VARIANT);
+            return _mField.variant;
+        }
+
+        bt2::OptionField optionField() const noexcept
+        {
+            BT_ASSERT_DBG(_mFieldType == _FieldType::OPTION);
+            return _mField.option;
+        }
+
+        bt2::ArrayField arrayField() const noexcept
+        {
+            BT_ASSERT_DBG(_mFieldType == _FieldType::ARRAY);
+            return _mField.array;
+        }
+
+        unsigned long long subFieldIndex() const noexcept
+        {
+            return _mSubFieldIndex;
+        }
+
+        void goToNextSubField() noexcept
+        {
+            /*
+             * We unconditionally increment `_mSubFieldIndex`, even if
+             * the current field is a variant/option field, because
+             * curSubField() doesn't care about `_mSubFieldIndex` for
+             * those cases anyway.
+             *
+             * In practice, `_mSubFieldIndex` will reach one with a
+             * variant/option field, but curSubField() will never be
+             * called with `_mSubFieldIndex` being something else than
+             * zero.
+             */
+            ++_mSubFieldIndex;
+        }
+
+        bt2::Field curSubField() noexcept
+        {
+            switch (_mFieldType) {
+            case _FieldType::STRUCT:
+                BT_ASSERT_DBG(_mSubFieldIndex < _mField.structure.cls().size());
+                return _mField.structure[_mSubFieldIndex];
+
+            case _FieldType::VARIANT:
+                BT_ASSERT_DBG(_mSubFieldIndex == 0);
+                return _mField.variant.selectedOptionField();
+
+            case _FieldType::OPTION:
+                BT_ASSERT_DBG(_mSubFieldIndex == 0);
+                BT_ASSERT_DBG(_mField.option.hasField());
+                return *_mField.option.field();
+
+            case _FieldType::ARRAY:
+                BT_ASSERT_DBG(_mSubFieldIndex < _mField.array.length());
+                return _mField.array[_mSubFieldIndex];
+
+            default:
+                bt_common_abort();
+            }
+        }
+
+        bt2::Field curSubFieldAndGoToNextSubField() noexcept
+        {
+            const auto field = this->curSubField();
+
+            this->goToNextSubField();
+            return field;
+        }
+
+    private:
+        /* Selector of `_mField` below */
+        enum class _FieldType
+        {
+            /* Selects `structure` */
+            STRUCT = 1,
+
+            /* Selects `variant` */
+            VARIANT,
+
+            /* Selects `option` */
+            OPTION,
+
+            /* Selects `array` */
+            ARRAY,
+        } _mFieldType;
+
+        /* Field of this frame, selected by `_mFieldType` above */
+        union _Field
+        {
+            explicit _Field(const bt2::StructureField field) noexcept
+            {
+                new (&structure) bt2::StructureField {field};
+            }
+
+            explicit _Field(const bt2::VariantField field) noexcept
+            {
+                new (&variant) bt2::VariantField {field};
+            }
+
+            explicit _Field(const bt2::OptionField field) noexcept
+            {
+                new (&option) bt2::OptionField {field};
+            }
+
+            explicit _Field(const bt2::ArrayField field) noexcept
+            {
+                new (&array) bt2::ArrayField {field};
+            }
+
+            static_assert(std::is_trivially_destructible<bt2::StructureField>::value,
+                          "`bt2::StructureField` is trivially destructible.");
+            static_assert(std::is_trivially_destructible<bt2::VariantField>::value,
+                          "`bt2::VariantField` is trivially destructible.");
+            static_assert(std::is_trivially_destructible<bt2::OptionField>::value,
+                          "`bt2::OptionField` is trivially destructible.");
+            static_assert(std::is_trivially_destructible<bt2::ArrayField>::value,
+                          "`bt2::ArrayField` is trivially destructible.");
+
+            bt2::StructureField structure;
+            bt2::VariantField variant;
+            bt2::OptionField option;
+            bt2::ArrayField array;
+        } _mField;
+
+        /*
+         * Index of, depending on `_mFieldType` above:
+         *
+         * `_FieldType::STRUCT`:
+         *     The current member of `_mField.structure`.
+         *
+         * `_FieldType::ARRAY`:
+         *     The current element field of `_mField.array`.
+         *
+         * `_FieldType::VARIANT`:
+         * `_FieldType::OPTION`:
+         *     Not applicable.
+         */
+        unsigned long long _mSubFieldIndex = 0;
+    };
+
+    /*
+     * Returns whether or not to ignore the field of `item`.
+     */
+    static bool _ignoreFieldItem(const FieldItem& item) noexcept
+    {
+        return !item.cls().libCls();
+    }
+
+    /*
+     * Handles the item `item`, changing the state accordingly, possibly
+     * adding one or more messages to `_mMsgs`.
+     */
+    void _handleItem(const Item& item);
+
+    /* Specific item handlers below */
+    void _handleItem(const ArrayFieldEndItem& item);
+    void _handleItem(const BlobFieldSectionItem& item);
+    void _handleItem(const DataStreamInfoItem& item);
+    void _handleItem(const DynLenArrayFieldBeginItem& item);
+    void _handleItem(const DynLenBlobFieldBeginItem& item);
+    void _handleItem(const BlobFieldEndItem& item);
+    void _handleItem(const EventRecordEndItem& item);
+    void _handleItem(const EventRecordInfoItem& item);
+    void _handleItem(const FixedLenBitArrayFieldItem& item);
+    void _handleItem(const FixedLenBoolFieldItem& item);
+    void _handleItem(const FixedLenFloatFieldItem& item);
+    void _handleItem(const FixedLenSIntFieldItem& item);
+    void _handleItem(const FixedLenUIntFieldItem& item);
+    void _handleItem(const MetadataStreamUuidItem& item);
+    void _handleItem(const NullTerminatedStrFieldBeginItem& item);
+    void _handleItem(const NullTerminatedStrFieldEndItem& item);
+    void _handleItem(const OptionalFieldBeginItem& item);
+    void _handleItem(const OptionalFieldEndItem& item);
+    void _handleItem(const PktBeginItem& item);
+    void _handleItem(const PktContentEndItem& item);
+    void _handleItem(const PktEndItem& item);
+    void _handleItem(const PktInfoItem& item);
+    void _handleItem(const PktMagicNumberItem& item);
+    void _handleItem(const ScopeBeginItem& item);
+    void _handleItem(const ScopeEndItem& item);
+    void _handleItem(const StaticLenArrayFieldBeginItem& item);
+    void _handleItem(const StaticLenBlobFieldBeginItem& item);
+    void _handleItem(const NonNullTerminatedStrFieldBeginItem& item);
+    void _handleItem(const NonNullTerminatedStrFieldEndItem& item);
+    void _handleItem(const StrFieldSubstrItem& item);
+    void _handleItem(const StructFieldBeginItem& item);
+    void _handleItem(const StructFieldEndItem& item);
+    void _handleItem(const VariantFieldBeginItem& item);
+    void _handleItem(const VariantFieldEndItem& item);
+    void _handleItem(const VarLenSEnumFieldItem& item);
+    void _handleItem(const VarLenSIntFieldItem& item);
+    void _handleItem(const VarLenUEnumFieldItem& item);
+    void _handleItem(const VarLenUIntFieldItem& item);
+    void _handleStrFieldBeginItem();
+    void _handleStrFieldEndItem();
+
+    template <typename ItemT>
+    void _handleUIntFieldItem(const ItemT& item)
+    {
+        if (_ignoreFieldItem(item)) {
+            return;
+        }
+
+        const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+        field.asUnsignedInteger() = item.val();
+    }
+
+    template <typename ItemT>
+    void _handleSIntFieldItem(const ItemT& item)
+    {
+        if (_ignoreFieldItem(item)) {
+            return;
+        }
+
+        const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+        field.asSignedInteger() = item.val();
+    }
+
+    /*
+     * Calls goToNextSubField() for the top stack frame.
+     */
+    void _stackTopGoToNextSubField()
+    {
+        BT_ASSERT_DBG(!_mStack.empty());
+        _mStack.top().goToNextSubField();
+    }
+
+    /*
+     * Returns curSubField() for the top stack frame.
+     */
+    bt2::Field _stackTopCurSubField()
+    {
+        BT_ASSERT_DBG(!_mStack.empty());
+        return _mStack.top().curSubField();
+    }
+
+    /*
+     * Returns curSubFieldAndGoToNextSubField() for the top stack frame.
+     */
+    bt2::Field _stackTopCurSubFieldAndGoToNextSubField()
+    {
+        BT_ASSERT_DBG(!_mStack.empty());
+        return _mStack.top().curSubFieldAndGoToNextSubField();
+    }
+
+    /*
+     * Pushes a stack frame managing `field` on the stack.
+     */
+    template <typename FieldT>
+    void _stackPush(const FieldT field)
+    {
+        _mStack.push(_StackFrame {field});
+    }
+
+    /*
+     * Removes the top stack frame.
+     */
+    void _stackPop()
+    {
+        BT_ASSERT_DBG(!_mStack.empty());
+        _mStack.pop();
+    }
+
+    /*
+     * Returns the current packet (weak) if there's one, or `nullptr`
+     * otherwise.
+     */
+    bt_packet *_curPkt()
+    {
+        if (_mCurPkt) {
+            return (*_mCurPkt)->libObjPtr();
+        }
+
+        return nullptr;
+    }
+
+    /*
+     * Sets the current packet to `pkt`.
+     */
+    void _curPkt(bt2::Packet::Shared pkt)
+    {
+        BT_ASSERT_DBG(!this->_curPkt());
+        _mCurPkt = std::move(pkt);
+    }
+
+    /*
+     * Resets the current packet.
+     */
+    void _resetCurPkt()
+    {
+        BT_ASSERT_DBG(this->_curPkt());
+        _mCurPkt.reset();
+    }
+
+    /*
+     * Creates and returns an initial discarded events message, not
+     * setting any specific count.
+     */
+    bt_message *_createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal);
+
+    /*
+     * Creates and returns an initial discarded packets message, not
+     * setting any specific count.
+     */
+    bt_message *_createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal);
+
+    /*
+     * Creates a packet end message and, if needed, updates the current
+     * timestamp.
+     */
+    bt_message *_createPktEndMsgAndUpdateCurDefClkVal();
+
+    /*
+     * Creates an event message using the class `cls` and having
+     * `defClkVal` as its timestamp.
+     */
+    bt_message *_createEventMsg(bt2::EventClass cls, const _OptUll& defClkVal);
+
+    /*
+     * Emits a packet beginning message having `defClkVal`, if set, as
+     * its default clock snapshot.
+     */
+    void _emitPktBeginMsg(const _OptUll& defClkVal);
+
+    /*
+     * Emits a delayed packet beginning message, considering the other
+     * timestamp `otherDefClkVal`.
+     */
+    void _emitDelayedPktBeginMsg(const _OptUll& otherDefClkVal);
+
+    /*
+     * Adds the message `msg` (ownership is transfered to this method)
+     * to the message queue.
+     */
+    void _addMsgToQueue(bt_message *msg);
+
+    /*
+     * Returns one of:
+     *
+     * A shared libbabeltrace2 message:
+     *     The next available message from the message queue,
+     *     removing it from the queue.
+     *
+     * `nonstd::nullopt`:
+     *     The message queue is empty.
+     */
+    nonstd::optional<bt2::ConstMessage::Shared> _releaseNextMsg();
+
+    /* libbabeltrace2 self message iterator to create messages (weak) */
+    bt_self_message_iterator *_mSelfMsgIter;
+
+    /* Corresponding libbabeltrace2 stream */
+    bt2::Stream _mStream;
+
+    /* Expected metadata stream UUID, if any */
+    nonstd::optional<bt2_common::Uuid> _mExpectedMetadataStreamUuid;
+
+    /* Quirks to fix */
+    MsgIterQuirks _mQuirks;
+
+    /* Underlying item sequence iterator to decode the data stream */
+    ItemSeqIter _mItemSeqIter;
+
+    /* Whether or not the iterator is ended */
+    bool _mIsDone = false;
+
+    /*
+     * Queue of already created messages.
+     *
+     * We're using a queue instead of keeping a single message because
+     * because one item (from `_mItemSeqIter`) may correspond to more
+     * than one libbabeltrace2 message.
+     */
+    std::queue<bt2::ConstMessage::Shared> _mMsgs;
+
+    /* Stack */
+    std::stack<_StackFrame> _mStack;
+
+    /* Root field of current scope */
+    nonstd::optional<bt2::StructureField> _mCurScopeField;
+
+    /*
+     * Whether or not to skip items until reaching the end of the
+     * current scope.
+     */
+    bool _mSkipItemsUntilScopeEndItem = false;
+
+    /*
+     * If set: a message that we're building, that's not yet ready to be
+     * returned.
+     */
+    nonstd::optional<bt2::Message::Shared> _mCurMsg;
+
+    /*
+     * If set: the current packet.
+     *
+     * Set while handling a `PktBeginItem` and reset while handling a
+     * `PktEndItem`.
+     */
+    nonstd::optional<bt2::Packet::Shared> _mCurPkt;
+
+    /* Current packet sequence number, if any */
+    _OptUll _mCurPktSeqNum;
+
+    /* Current default clock value, if any */
+    _OptUll _mCurDefClkVal;
+
+    /* Current discarded event record counter snapshot, if any */
+    _OptUll _mCurDiscErCounterSnap;
+
+    /*
+     * Values of the beginning and end timestamps of the current packet.
+     *
+     * Set while handling a `PktInfoItem` and reset while handling a
+     * `PktEndItem`.
+     */
+    _OptUll _mPktBeginDefClkVal;
+    _OptUll _mPktEndDefClkVal;
+
+    /*
+     * Whether or not a stream beginning message was provided to the
+     * user.
+     */
+    bool _mEmittedStreamBeginMsg = false;
+
+    /*
+     * Whether or not to delay the emission of a packet beginning
+     * message.
+     */
+    bool _mDelayPktBeginMsgEmission = false;
+
+    /*
+     * Whether or not, while processing `StrFieldSubstrItem` items, we
+     * got a null character.
+     */
+    bool _mHaveStrFieldSubstrItemNullChar = false;
+
+    /*
+     * Current BLOB field data offset while processing BLOB field
+     * section items.
+     */
+    std::size_t _mCurBlobFieldDataOffset = 0;
+
+    /* Logging configuration */
+    bt2_common::LogCfg _mLogCfg;
+
+    /* Helper to log items */
+    LoggingItemVisitor _mLoggingVisitor;
+};
+
+} /* namespace src */
+} /* namespace ctf */
+
+#endif /* CTF_COMMON_SRC_MSG_ITER_HPP */
This page took 0.041927 seconds and 5 git commands to generate.