--- /dev/null
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 Simon Marchi <simon.marchi@efficios.com>
+ * Copyright (c) 2015-2024 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <algorithm>
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/bt2c/aliases.hpp"
+#include "cpp-common/bt2c/call.hpp"
+#include "cpp-common/bt2c/fmt.hpp"
+#include "cpp-common/vendor/fmt/format.h"
+
+#include "item-seq/item.hpp"
+#include "msg-iter.hpp"
+#include "plugins/ctf/common/src/metadata/ctf-ir.hpp"
+
+namespace ctf {
+namespace src {
+
+using namespace bt2c::literals::datalen;
+
+MsgIter::MsgIter(const bt2::SelfMessageIterator selfMsgIter, const ctf::src::TraceCls& traceCls,
+ bt2s::optional<bt2c::Uuid> expectedMetadataStreamUuid, const bt2::Stream stream,
+ Medium::UP medium, const MsgIterQuirks& quirks, const bt2c::Logger& parentLogger) :
+ _mLogger {parentLogger, "PLUGIN/CTF/MSG-ITER"},
+ _mSelfMsgIter {selfMsgIter}, _mStream {stream},
+ _mExpectedMetadataStreamUuid {std::move(expectedMetadataStreamUuid)}, _mQuirks {quirks},
+ _mItemSeqIter {std::move(medium), traceCls, _mLogger}, _mUnicodeConv {_mLogger},
+ _mLoggingVisitor {"Handling item", _mLogger}
+{
+ BT_CPPLOGD("Created CTF plugin message iterator: "
+ "addr={}, trace-cls-addr={}, log-level={}",
+ fmt::ptr(this), fmt::ptr(&traceCls), _mLogger.level());
+}
+
+bt2::ConstMessage::Shared MsgIter::next()
+{
+ BT_CPPLOGD("Getting next message: addr={}", fmt::ptr(this));
+
+ if (_mIsDone) {
+ return bt2::ConstMessage::Shared {};
+ }
+
+ /*
+ * 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.
+ */
+ if (const auto item = _mItemSeqIter.next()) {
+ /* Handle item if needed */
+ if (!_mSkipItemsUntilScopeEndItem || item->isScopeEnd()) {
+ this->_handleItem(*item);
+
+ if (auto msg = this->_releaseNextMsg()) {
+ return msg;
+ }
+ }
+ } else {
+ /* No more items: this is the end! */
+ break;
+ }
+ }
+
+ /* We're done! */
+ _mIsDone = true;
+ return _mSelfMsgIter.createStreamEndMessage(_mStream);
+ } catch (const bt2c::Error&) {
+ BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW("Failed to create the next message: addr={}",
+ fmt::ptr(this));
+ }
+}
+
+void MsgIter::_handleItem(const Item& item)
+{
+ /* Log item details */
+ if (_mLogger.wouldLogT()) {
+ item.accept(_mLoggingVisitor);
+ }
+
+ /* Defer to specific handler */
+ switch (item.type()) {
+ case Item::Type::PktBegin:
+ this->_handleItem(item.asPktBegin());
+ break;
+ case Item::Type::PktEnd:
+ this->_handleItem(item.asPktEnd());
+ break;
+ case Item::Type::ScopeBegin:
+ this->_handleItem(item.asScopeBegin());
+ break;
+ case Item::Type::ScopeEnd:
+ this->_handleItem(item.asScopeEnd());
+ break;
+ case Item::Type::PktContentEnd:
+ this->_handleItem(item.asPktContentEnd());
+ break;
+ case Item::Type::EventRecordEnd:
+ this->_handleItem(item.asEventRecordEnd());
+ break;
+ case Item::Type::PktMagicNumber:
+ this->_handleItem(item.asPktMagicNumber());
+ break;
+ case Item::Type::MetadataStreamUuid:
+ this->_handleItem(item.asMetadataStreamUuid());
+ break;
+ case Item::Type::DataStreamInfo:
+ this->_handleItem(item.asDataStreamInfo());
+ break;
+ case Item::Type::PktInfo:
+ this->_handleItem(item.asPktInfo());
+ break;
+ case Item::Type::EventRecordInfo:
+ this->_handleItem(item.asEventRecordInfo());
+ break;
+ case Item::Type::FixedLenBitArrayField:
+ case Item::Type::FixedLenBitMapField:
+ this->_handleItem(item.asFixedLenBitArrayField());
+ break;
+ case Item::Type::FixedLenBoolField:
+ this->_handleItem(item.asFixedLenBoolField());
+ break;
+ case Item::Type::FixedLenSIntField:
+ this->_handleItem(item.asFixedLenSIntField());
+ break;
+ case Item::Type::FixedLenUIntField:
+ this->_handleItem(item.asFixedLenUIntField());
+ break;
+ case Item::Type::FixedLenFloatField:
+ this->_handleItem(item.asFixedLenFloatField());
+ break;
+ case Item::Type::VarLenSIntField:
+ this->_handleItem(item.asVarLenSIntField());
+ break;
+ case Item::Type::VarLenUIntField:
+ this->_handleItem(item.asVarLenUIntField());
+ break;
+ case Item::Type::NullTerminatedStrFieldBegin:
+ this->_handleItem(item.asNullTerminatedStrFieldBegin());
+ break;
+ case Item::Type::NullTerminatedStrFieldEnd:
+ this->_handleItem(item.asNullTerminatedStrFieldEnd());
+ break;
+ case Item::Type::RawData:
+ this->_handleItem(item.asRawData());
+ break;
+ case Item::Type::StructFieldBegin:
+ this->_handleItem(item.asStructFieldBegin());
+ break;
+ case Item::Type::StructFieldEnd:
+ this->_handleItem(item.asStructFieldEnd());
+ break;
+ case Item::Type::StaticLenArrayFieldBegin:
+ this->_handleItem(item.asStaticLenArrayFieldBegin());
+ break;
+ case Item::Type::StaticLenArrayFieldEnd:
+ case Item::Type::DynLenArrayFieldEnd:
+ this->_handleItem(item.asArrayFieldEnd());
+ break;
+ case Item::Type::DynLenArrayFieldBegin:
+ this->_handleItem(item.asDynLenArrayFieldBegin());
+ break;
+ case Item::Type::StaticLenBlobFieldBegin:
+ this->_handleItem(item.asStaticLenBlobFieldBegin());
+ break;
+ case Item::Type::StaticLenBlobFieldEnd:
+ case Item::Type::DynLenBlobFieldEnd:
+ this->_handleItem(item.asBlobFieldEnd());
+ break;
+ case Item::Type::DynLenBlobFieldBegin:
+ this->_handleItem(item.asDynLenBlobFieldBegin());
+ break;
+ case Item::Type::StaticLenStrFieldBegin:
+ case Item::Type::DynLenStrFieldBegin:
+ this->_handleItem(item.asNonNullTerminatedStrFieldBegin());
+ break;
+ case Item::Type::StaticLenStrFieldEnd:
+ case Item::Type::DynLenStrFieldEnd:
+ this->_handleItem(item.asNonNullTerminatedStrFieldEnd());
+ break;
+ case Item::Type::VariantFieldWithSIntSelBegin:
+ case Item::Type::VariantFieldWithUIntSelBegin:
+ this->_handleItem(item.asVariantFieldBegin());
+ break;
+ case Item::Type::VariantFieldWithSIntSelEnd:
+ case Item::Type::VariantFieldWithUIntSelEnd:
+ this->_handleItem(item.asVariantFieldEnd());
+ break;
+ case Item::Type::OptionalFieldWithBoolSelBegin:
+ case Item::Type::OptionalFieldWithSIntSelBegin:
+ case Item::Type::OptionalFieldWithUIntSelBegin:
+ this->_handleItem(item.asOptionalFieldBegin());
+ break;
+ case Item::Type::OptionalFieldWithBoolSelEnd:
+ case Item::Type::OptionalFieldWithSIntSelEnd:
+ case Item::Type::OptionalFieldWithUIntSelEnd:
+ this->_handleItem(item.asOptionalFieldEnd());
+ break;
+ default:
+ BT_CPPLOGT("Skipping item.");
+ return;
+ }
+}
+
+void MsgIter::_handleItem(const PktBeginItem&)
+{
+ BT_ASSERT_DBG(!_mCurPkt);
+ this->_curPkt(_mStream.createPacket());
+}
+
+bt2::Message::Shared MsgIter::_createPktEndMsgAndUpdateCurDefClkVal()
+{
+ BT_ASSERT_DBG(_mCurPkt);
+
+ 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 _mSelfMsgIter.createPacketEndMessage(*_mCurPkt, defClkVal);
+ } else {
+ return _mSelfMsgIter.createPacketEndMessage(*_mCurPkt);
+ }
+}
+
+void MsgIter::_handleItem(const PktEndItem&)
+{
+ BT_ASSERT_DBG(!_mCurMsg);
+ BT_ASSERT_DBG(_mCurPkt);
+
+ /* 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 Scope::PktHeader:
+ /* Nothing needed from the packet header: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ break;
+ case Scope::PktCtx:
+ {
+ BT_ASSERT_DBG(_mCurPkt);
+
+ if (const auto pktCtxField = _mCurPkt->contextField()) {
+ _mCurScopeField = pktCtxField;
+ } else {
+ /* Nothing needed from the packet context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case Scope::EventRecordHeader:
+ /* Nothing needed from the event record header: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ break;
+ case Scope::CommonEventRecordCtx:
+ {
+ BT_ASSERT_DBG(_mCurMsg);
+
+ if (const auto commonCtxField = _mCurMsg->asEvent().event().commonContextField()) {
+ _mCurScopeField = commonCtxField;
+ } else {
+ /* Nothing needed from the common context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case Scope::SpecEventRecordCtx:
+ {
+ BT_ASSERT_DBG(_mCurMsg);
+
+ if (const auto specCtxField = _mCurMsg->asEvent().event().specificContextField()) {
+ _mCurScopeField = specCtxField;
+ } else {
+ /* Nothing needed from the specific context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case Scope::EventRecordPayload:
+ {
+ BT_ASSERT_DBG(_mCurMsg);
+
+ if (const auto payloadField = _mCurMsg->asEvent().event().payloadField()) {
+ _mCurScopeField = 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(_mCurPkt);
+}
+
+void MsgIter::_handleItem(const EventRecordEndItem&)
+{
+ BT_ASSERT_DBG(_mStack.empty());
+ BT_ASSERT_DBG(_mCurMsg);
+
+ /* Emit current message (move to message queue) */
+ _mMsgs.emplace(std::move(_mCurMsg));
+ BT_ASSERT_DBG(!_mCurMsg);
+}
+
+void MsgIter::_handleItem(const PktMagicNumberItem& item)
+{
+ if (!item.isValid()) {
+ BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+ bt2c::Error, "Invalid packet magic number: val={:#x}, expected-val={:#x}", item.val(),
+ item.expectedVal());
+ }
+}
+
+void MsgIter::_handleItem(const MetadataStreamUuidItem& item)
+{
+ BT_ASSERT_DBG(_mExpectedMetadataStreamUuid);
+
+ if (item.uuid() != *_mExpectedMetadataStreamUuid) {
+ BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2c::Error,
+ "Invalid metadata stream UUID: uuid={}, expected-uuid={}",
+ item.uuid(), *_mExpectedMetadataStreamUuid);
+ }
+}
+
+void MsgIter::_handleItem(const DataStreamInfoItem& item)
+{
+ /*
+ * `_mItemSeqIter` doesn't care about contiguous packets from the
+ * same medium belonging to different data streams, but this message
+ * iterator does because it manages a single libbabeltrace2 stream.
+ */
+ BT_ASSERT_DBG(item.cls());
+
+ if (item.cls()->id() != _mStream.cls().id()) {
+ BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+ bt2c::Error,
+ "Two contiguous packets belong to data streams having different classes: "
+ "expected-data-stream-class-class-id={}, data-stream-class-id={}",
+ item.cls()->id(), _mStream.cls().id());
+ }
+
+ if (item.id() && *item.id() != _mStream.id()) {
+ BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+ bt2c::Error,
+ "Two contiguous packets belong to different data streams: "
+ "expected-data-stream-id={}, data-stream-id={}",
+ *item.id(), _mStream.id());
+ }
+
+ if (!_mEmittedStreamBeginMsg) {
+ this->_addMsgToQueue(_mSelfMsgIter.createStreamBeginningMessage(_mStream));
+ _mEmittedStreamBeginMsg = true;
+ }
+}
+
+bt2::Message::Shared 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 _mSelfMsgIter.createDiscardedEventsMessage(_mStream, *prevPktEndDefClkVal,
+ *_mPktEndDefClkVal);
+ } else {
+ return _mSelfMsgIter.createDiscardedEventsMessage(_mStream);
+ }
+}
+
+bt2::Message::Shared 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 _mSelfMsgIter.createDiscardedPacketsMessage(_mStream, *prevPktEndDefClkVal,
+ *_mPktBeginDefClkVal);
+ } else {
+ return _mSelfMsgIter.createDiscardedPacketsMessage(_mStream);
+ }
+}
+
+void MsgIter::_emitPktBeginMsg(const _OptUll& defClkVal)
+{
+ BT_ASSERT_DBG(_mCurPkt);
+
+ /* Add new message to queue */
+ this->_addMsgToQueue(bt2c::call([this, &defClkVal] {
+ if (defClkVal) {
+ _mCurDefClkVal = defClkVal;
+ return _mSelfMsgIter.createPacketBeginningMessage(*_mCurPkt, *defClkVal);
+ } else {
+ return _mSelfMsgIter.createPacketBeginningMessage(*_mCurPkt);
+ }
+ }));
+}
+
+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.
+ *
+ * Emit a packet beginning message now.
+ */
+ this->_emitPktBeginMsg(bt2c::call([this, &otherDefClkVal]() -> _OptUll {
+ if (_mPktBeginDefClkVal && otherDefClkVal) {
+ return std::min(*_mPktBeginDefClkVal, *otherDefClkVal);
+ } else if (_mPktBeginDefClkVal) {
+ return _mPktBeginDefClkVal;
+ } else if (otherDefClkVal) {
+ return otherDefClkVal;
+ }
+
+ return bt2s::nullopt;
+ }));
+}
+
+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);
+
+ // TODO: handle `*discErCounterSnap` being <= `*_mCurDiscErCounterSnap`
+ if (*discErCounterSnap > *_mCurDiscErCounterSnap) {
+ /* Create and initialize the message */
+ auto msg = this->_createInitDiscEventsMsg(prevPktEndDefClkVal);
+
+ /* Set its count */
+ msg->asDiscardedEvents().count(*discErCounterSnap - *_mCurDiscErCounterSnap);
+
+ /* Add to queue */
+ this->_addMsgToQueue(std::move(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);
+
+ // TODO: handle `*seqNum` being <= `*_mCurPktSeqNum`
+ if (*_mCurPktSeqNum + 1 < *seqNum) {
+ /* Create and initialize the message */
+ const auto msg = this->_createInitDiscPktsMsg(prevPktEndDefClkVal);
+
+ /* Set its count */
+ msg->asDiscardedPackets().count(*seqNum - *_mCurPktSeqNum - 1);
+
+ /* Add to queue */
+ this->_addMsgToQueue(std::move(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);
+ }
+}
+
+bt2::Message::Shared MsgIter::_createEventMsg(const bt2::EventClass cls, const _OptUll& defClkVal)
+{
+ if (defClkVal) {
+ if (_mCurPkt) {
+ return _mSelfMsgIter.createEventMessage(cls, *_mCurPkt, *defClkVal);
+ } else {
+ return _mSelfMsgIter.createEventMessage(cls, _mStream, *defClkVal);
+ }
+ } else {
+ if (_mCurPkt) {
+ return _mSelfMsgIter.createEventMessage(cls, *_mCurPkt);
+ } else {
+ return _mSelfMsgIter.createEventMessage(cls, _mStream);
+ }
+ }
+}
+
+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 = this->_createEventMsg(*item.cls()->libCls(), item.defClkVal());
+}
+
+void MsgIter::_handleItem(const FixedLenBitArrayFieldItem& item)
+{
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ this->_stackTopCurSubFieldAndGoToNextSubField().asBitArray().valueAsInteger(item.uIntVal());
+}
+
+void MsgIter::_handleItem(const FixedLenBoolFieldItem& item)
+{
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ this->_stackTopCurSubFieldAndGoToNextSubField().asBool().value(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().value(item.val());
+ } else {
+ BT_ASSERT_DBG(item.cls().len() == 64_bits);
+ field.asDoublePrecisionReal().value(item.val());
+ }
+}
+
+void MsgIter::_handleItem(const VarLenSIntFieldItem& item)
+{
+ this->_handleSIntFieldItem(item);
+}
+
+void MsgIter::_handleItem(const VarLenUIntFieldItem& item)
+{
+ this->_handleUIntFieldItem(item);
+}
+
+void MsgIter::_handleStrFieldBeginItem(const FieldItem& item)
+{
+ this->_stackTopCurSubField().asString().value("");
+ _mHaveNullChar = false;
+ _mUtf16NullCpFinder = NullCpFinder<2> {};
+ _mUtf32NullCpFinder = NullCpFinder<4> {};
+ _mStrBuf.clear();
+ _mCurStrFieldEncoding = item.cls().asStr().encoding();
+}
+
+void MsgIter::_handleStrFieldEndItem()
+{
+ switch (_mCurStrFieldEncoding) {
+ case StrEncoding::Utf16Be:
+ case StrEncoding::Utf16Le:
+ case StrEncoding::Utf32Be:
+ case StrEncoding::Utf32Le:
+ {
+ /* Convert to UTF-8 */
+ const auto utf8Str = bt2c::call([this] {
+ bt2c::ConstBytes inBytes {_mStrBuf.begin(), _mStrBuf.end()};
+
+ switch (_mCurStrFieldEncoding) {
+ case StrEncoding::Utf16Be:
+ return _mUnicodeConv.utf8FromUtf16Be(inBytes);
+ case StrEncoding::Utf16Le:
+ return _mUnicodeConv.utf8FromUtf16Le(inBytes);
+ case StrEncoding::Utf32Be:
+ return _mUnicodeConv.utf8FromUtf32Be(inBytes);
+ case StrEncoding::Utf32Le:
+ return _mUnicodeConv.utf8FromUtf32Le(inBytes);
+ default:
+ bt_common_abort();
+ }
+ });
+ const auto endIt =
+ !utf8Str.empty() && utf8Str.back() == 0 ? utf8Str.end() - 1 : utf8Str.end();
+
+ /* Append */
+ this->_stackTopCurSubField().asString().append(
+ reinterpret_cast<const char *>(utf8Str.data()), endIt - utf8Str.begin());
+ }
+
+ default:
+ break;
+ }
+
+ this->_stackTopGoToNextSubField();
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldBeginItem& item)
+{
+ this->_handleStrFieldBeginItem(item);
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldEndItem&)
+{
+ this->_handleStrFieldEndItem();
+}
+
+void MsgIter::_handleBlobRawDataItem(const RawDataItem& item)
+{
+ std::memcpy(&this->_stackTopCurSubField().asBlob().data()[_mCurBlobFieldDataOffset],
+ item.data().begin(), item.data().size());
+ _mCurBlobFieldDataOffset += item.data().size();
+}
+
+void MsgIter::_handleStrRawDataItem(const RawDataItem& item)
+{
+ if (_mHaveNullChar) {
+ /* No more text data */
+ return;
+ }
+
+ if (_mCurStrFieldEncoding == StrEncoding::Utf8) {
+ /* Try to find the first U+0000 codepoint */
+ const auto endIt = std::find(item.data().begin(), item.data().end(), 0);
+ _mHaveNullChar = endIt != item.data().end();
+
+ /* Append to current string field */
+ this->_stackTopCurSubField().asString().append(
+ reinterpret_cast<const char *>(item.data().data()), endIt - item.data().begin());
+ } else {
+ /* Try to find the first U+0000 codepoint */
+ auto endIt = item.data().end();
+ const auto afterNullCpIt = bt2c::call([this, &item] {
+ if (_mCurStrFieldEncoding == StrEncoding::Utf16Be ||
+ _mCurStrFieldEncoding == StrEncoding::Utf16Le) {
+ return _mUtf16NullCpFinder.findNullCp(item.data());
+ } else {
+ BT_ASSERT_DBG(_mCurStrFieldEncoding == StrEncoding::Utf32Be ||
+ _mCurStrFieldEncoding == StrEncoding::Utf32Le);
+ return _mUtf32NullCpFinder.findNullCp(item.data());
+ }
+ });
+
+ if (afterNullCpIt) {
+ /* Found U+0000 */
+ endIt = *afterNullCpIt;
+ _mHaveNullChar = true;
+ }
+
+ /* Append to current string buffer */
+ _mStrBuf.insert(_mStrBuf.end(), item.data().begin(), endIt);
+ }
+}
+
+void MsgIter::_handleItem(const RawDataItem& item)
+{
+ if (this->_stackTopCurSubField().isString()) {
+ this->_handleStrRawDataItem(item);
+ } else {
+ BT_ASSERT_DBG(this->_stackTopCurSubField().isBlob());
+ this->_handleBlobRawDataItem(item);
+ }
+}
+
+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&)
+{
+ _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& item)
+{
+ this->_handleStrFieldBeginItem(item);
+}
+
+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&)
+{
+ this->_stackPop();
+}
+
+void MsgIter::_addMsgToQueue(bt2::ConstMessage::Shared msg)
+{
+ _mMsgs.emplace(std::move(msg));
+}
+
+bt2::ConstMessage::Shared MsgIter::_releaseNextMsg()
+{
+ if (_mMsgs.empty()) {
+ return bt2::ConstMessage::Shared {};
+ }
+
+ auto msg = std::move(_mMsgs.front());
+
+ _mMsgs.pop();
+ return msg;
+}
+
+} /* namespace src */
+} /* namespace ctf */
--- /dev/null
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022-2024 EfficiOS, Inc
+ */
+
+#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP
+#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP
+
+#include <queue>
+#include <stack>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/assert.h"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/self-message-iterator.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/bt2c/aliases.hpp"
+#include "cpp-common/bt2c/unicode-conv.hpp"
+
+#include "item-seq/item-seq-iter.hpp"
+#include "item-seq/item-visitor.hpp"
+#include "item-seq/logging-item-visitor.hpp"
+#include "null-cp-finder.hpp"
+#include "plugins/ctf/common/src/metadata/ctf-ir.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 data 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
+ * `bt2c::TryAgain` or a medium error.
+ */
+ explicit MsgIter(bt2::SelfMessageIterator selfMsgIter, const ctf::src::TraceCls& traceCls,
+ bt2s::optional<bt2c::Uuid> expectedMetadataStreamUuid, bt2::Stream stream,
+ Medium::UP medium, const MsgIterQuirks& quirks,
+ const bt2c::Logger& parentLogger);
+
+ /* 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.
+ *
+ * `bt2s::nullopt`:
+ * The iterator is ended.
+ *
+ * May throw whatever Medium::buf() may throw as well
+ * as `bt2c::Error`.
+ */
+ bt2::ConstMessage::Shared next();
+
+private:
+ /* An optional `unsigned long long` value */
+ using _OptUll = bt2s::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().length());
+ 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 BlobFieldEndItem& item);
+ void _handleItem(const DataStreamInfoItem& item);
+ void _handleItem(const DynLenArrayFieldBeginItem& item);
+ void _handleItem(const DynLenBlobFieldBeginItem& 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 NonNullTerminatedStrFieldBeginItem& item);
+ void _handleItem(const NonNullTerminatedStrFieldEndItem& 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 _handleBlobRawDataItem(const RawDataItem& item);
+ void _handleStrRawDataItem(const RawDataItem& item);
+ void _handleItem(const RawDataItem& item);
+ void _handleItem(const ScopeBeginItem& item);
+ void _handleItem(const ScopeEndItem& item);
+ void _handleItem(const StaticLenArrayFieldBeginItem& item);
+ void _handleItem(const StaticLenBlobFieldBeginItem& item);
+ void _handleItem(const StructFieldBeginItem& item);
+ void _handleItem(const StructFieldEndItem& item);
+ void _handleItem(const VariantFieldBeginItem& item);
+ void _handleItem(const VariantFieldEndItem& item);
+ void _handleItem(const VarLenSIntFieldItem& item);
+ void _handleItem(const VarLenUIntFieldItem& item);
+ void _handleStrFieldBeginItem(const FieldItem& item);
+ void _handleStrFieldEndItem();
+
+ template <typename ItemT>
+ void _handleUIntFieldItem(const ItemT& item)
+ {
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+ field.asUnsignedInteger().value(item.val());
+ }
+
+ template <typename ItemT>
+ void _handleSIntFieldItem(const ItemT& item)
+ {
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+ field.asSignedInteger().value(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();
+ }
+
+ /*
+ * Sets the current packet to `pkt`.
+ */
+ void _curPkt(bt2::Packet::Shared pkt)
+ {
+ BT_ASSERT_DBG(!_mCurPkt);
+ _mCurPkt = std::move(pkt);
+ }
+
+ /*
+ * Resets the current packet.
+ */
+ void _resetCurPkt()
+ {
+ BT_ASSERT_DBG(_mCurPkt);
+ _mCurPkt.reset();
+ }
+
+ /*
+ * Creates and returns an initial discarded events message, not
+ * setting any specific count.
+ */
+ bt2::Message::Shared _createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal);
+
+ /*
+ * Creates and returns an initial discarded packets message, not
+ * setting any specific count.
+ */
+ bt2::Message::Shared _createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal);
+
+ /*
+ * Creates a packet end message and, if needed, updates the current
+ * timestamp.
+ */
+ bt2::Message::Shared _createPktEndMsgAndUpdateCurDefClkVal();
+
+ /*
+ * Creates an event message using the class `cls` and having
+ * `defClkVal` as its timestamp.
+ */
+ bt2::Message::Shared _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` to the message queue.
+ */
+ void _addMsgToQueue(bt2::ConstMessage::Shared msg);
+
+ /*
+ * Returns one of:
+ *
+ * A shared libbabeltrace2 message:
+ * The next available message from the message queue, removing
+ * it from the queue.
+ *
+ * `bt2s::nullopt`:
+ * The message queue is empty.
+ */
+ bt2::ConstMessage::Shared _releaseNextMsg();
+
+ /* Logger */
+ bt2c::Logger _mLogger;
+
+ /* libbabeltrace2 self message iterator to create messages (weak) */
+ bt2::SelfMessageIterator _mSelfMsgIter;
+
+ /* Corresponding libbabeltrace2 stream */
+ bt2::Stream _mStream;
+
+ /* Expected metadata stream UUID, if any */
+ bt2s::optional<bt2c::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 */
+ bt2::OptionalBorrowedObject<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.
+ */
+ bt2::Message::Shared _mCurMsg;
+
+ /*
+ * If set: the current packet.
+ *
+ * Set while handling a `PktBeginItem` and reset while handling a
+ * `PktEndItem`.
+ */
+ 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 `RawDataItem` items, we got a
+ * null character.
+ */
+ bool _mHaveNullChar = false;
+
+ /* Null codepoint finders for UTF-16 and UTF-32 */
+ NullCpFinder<2> _mUtf16NullCpFinder;
+ NullCpFinder<4> _mUtf32NullCpFinder;
+
+ /* Unicode converter to decode UTF-16 and UTF-32 strings */
+ bt2c::UnicodeConv _mUnicodeConv;
+
+ /* Buffer holding the string to convert to UTF-8 */
+ std::vector<std::uint8_t> _mStrBuf;
+
+ /*
+ * Current BLOB field data offset while processing BLOB field
+ * section items.
+ */
+ std::size_t _mCurBlobFieldDataOffset = 0;
+
+ /*
+ * Current string field encoding, if any.
+ */
+ StrEncoding _mCurStrFieldEncoding = StrEncoding::Utf8;
+
+ /* Helper to log items */
+ LoggingItemVisitor _mLoggingVisitor;
+};
+
+} /* namespace src */
+} /* namespace ctf */
+
+#endif /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP */