+void bt_self_message_iterator_configuration_set_can_seek_forward(
+ bt_self_message_iterator_configuration *config,
+ bt_bool can_seek_forward)
+{
+ BT_ASSERT_PRE_NON_NULL(config, "Message iterator configuration");
+ BT_ASSERT_PRE_DEV_HOT(config, "Message iterator configuration", "");
+
+ config->can_seek_forward = can_seek_forward;
+}
+
+/*
+ * Validate that the default clock snapshot in `msg` doesn't make us go back in
+ * time.
+ */
+
+BT_ASSERT_POST_DEV_FUNC
+static
+bool clock_snapshots_are_monotonic_one(
+ struct bt_self_component_port_input_message_iterator *iterator,
+ const bt_message *msg)
+{
+ const struct bt_clock_snapshot *clock_snapshot = NULL;
+ bt_message_type message_type = bt_message_get_type(msg);
+ int64_t ns_from_origin;
+ enum bt_clock_snapshot_get_ns_from_origin_status clock_snapshot_status;
+
+ /*
+ * The default is true: if we can't figure out the clock snapshot
+ * (or there is none), assume it is fine.
+ */
+ bool result = true;
+
+ switch (message_type) {
+ case BT_MESSAGE_TYPE_EVENT:
+ {
+ struct bt_message_event *event_msg = (struct bt_message_event *) msg;
+ clock_snapshot = event_msg->default_cs;
+ break;
+ }
+ case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
+ {
+ struct bt_message_message_iterator_inactivity *inactivity_msg =
+ (struct bt_message_message_iterator_inactivity *) msg;
+ clock_snapshot = inactivity_msg->default_cs;
+ break;
+ }
+ case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+ case BT_MESSAGE_TYPE_PACKET_END:
+ {
+ struct bt_message_packet *packet_msg = (struct bt_message_packet *) msg;
+ clock_snapshot = packet_msg->default_cs;
+ break;
+ }
+ case BT_MESSAGE_TYPE_STREAM_BEGINNING:
+ case BT_MESSAGE_TYPE_STREAM_END:
+ {
+ struct bt_message_stream *stream_msg = (struct bt_message_stream *) msg;
+ if (stream_msg->default_cs_state != BT_MESSAGE_STREAM_CLOCK_SNAPSHOT_STATE_KNOWN) {
+ goto end;
+ }
+
+ clock_snapshot = stream_msg->default_cs;
+ break;
+ }
+ case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+ case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+ {
+ struct bt_message_discarded_items *discarded_msg =
+ (struct bt_message_discarded_items *) msg;
+
+ clock_snapshot = discarded_msg->default_begin_cs;
+ break;
+ }
+ }
+
+ if (!clock_snapshot) {
+ goto end;
+ }
+
+ clock_snapshot_status = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, &ns_from_origin);
+ if (clock_snapshot_status != BT_FUNC_STATUS_OK) {
+ goto end;
+ }
+
+ result = ns_from_origin >= iterator->last_ns_from_origin;
+ iterator->last_ns_from_origin = ns_from_origin;
+end:
+ return result;
+}
+
+BT_ASSERT_POST_DEV_FUNC
+static
+bool clock_snapshots_are_monotonic(
+ struct bt_self_component_port_input_message_iterator *iterator,
+ bt_message_array_const msgs, uint64_t msg_count)
+{
+ uint64_t i;
+ bool result;
+
+ for (i = 0; i < msg_count; i++) {
+ if (!clock_snapshots_are_monotonic_one(iterator, msgs[i])) {
+ result = false;
+ goto end;
+ }
+ }
+
+ result = true;
+
+end:
+ return result;
+}
+
+/*
+ * When a new stream begins, verify that the clock class tied to this
+ * stream is compatible with what we've seen before.
+ */
+
+BT_ASSERT_POST_DEV_FUNC
+static
+bool clock_classes_are_compatible_one(struct bt_self_component_port_input_message_iterator *iterator,
+ const struct bt_message *msg)
+{
+ enum bt_message_type message_type = bt_message_get_type(msg);
+ bool result;
+
+ if (message_type == BT_MESSAGE_TYPE_STREAM_BEGINNING) {
+ const struct bt_message_stream *stream_msg = (struct bt_message_stream *) msg;
+ const struct bt_clock_class *clock_class = stream_msg->stream->class->default_clock_class;
+ bt_uuid clock_class_uuid = NULL;
+
+ if (clock_class) {
+ clock_class_uuid = bt_clock_class_get_uuid(clock_class);
+ }
+
+ switch (iterator->clock_expectation.type) {
+ case CLOCK_EXPECTATION_UNSET:
+ /*
+ * This is the first time we see a message with a clock
+ * snapshot: record the properties of that clock, against
+ * which we'll compare the clock properties of the following
+ * messages.
+ */
+
+ if (!clock_class) {
+ iterator->clock_expectation.type = CLOCK_EXPECTATION_NONE;
+ } else if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
+ iterator->clock_expectation.type = CLOCK_EXPECTATION_ORIGIN_UNIX;
+ } else if (clock_class_uuid) {
+ iterator->clock_expectation.type = CLOCK_EXPECTATION_ORIGIN_OTHER_UUID;
+ bt_uuid_copy(iterator->clock_expectation.uuid, clock_class_uuid);
+ } else {
+ iterator->clock_expectation.type = CLOCK_EXPECTATION_ORIGIN_OTHER_NO_UUID;
+ }
+ break;
+
+ case CLOCK_EXPECTATION_NONE:
+ if (clock_class) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting no clock class, got one: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+
+ break;
+
+ case CLOCK_EXPECTATION_ORIGIN_UNIX:
+ if (!clock_class) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class, got none.");
+ result = false;
+ goto end;
+ }
+
+ if (!bt_clock_class_origin_is_unix_epoch(clock_class)) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class with Unix epoch origin: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+ break;
+
+ case CLOCK_EXPECTATION_ORIGIN_OTHER_UUID:
+ if (!clock_class) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class, got none.");
+ result = false;
+ goto end;
+ }
+
+ if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class without Unix epoch origin: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+
+ if (!clock_class_uuid) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class with UUID: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+
+ if (bt_uuid_compare(iterator->clock_expectation.uuid, clock_class_uuid)) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class with UUID, got one "
+ "with a different UUID: %![cc-]+K, expected-uuid=%!u",
+ clock_class, iterator->clock_expectation.uuid);
+ result = false;
+ goto end;
+ }
+ break;
+
+ case CLOCK_EXPECTATION_ORIGIN_OTHER_NO_UUID:
+ if (!clock_class) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class, got none.");
+ result = false;
+ goto end;
+ }
+
+ if (bt_clock_class_origin_is_unix_epoch(clock_class)) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class without Unix epoch origin: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+
+ if (clock_class_uuid) {
+ BT_ASSERT_POST_DEV_MSG(
+ "Expecting a clock class without UUID: %![cc-]+K",
+ clock_class);
+ result = false;
+ goto end;
+ }
+ break;
+ }
+ }
+
+ result = true;
+
+end:
+ return result;
+}
+
+BT_ASSERT_POST_DEV_FUNC
+static
+bool clock_classes_are_compatible(
+ struct bt_self_component_port_input_message_iterator *iterator,
+ bt_message_array_const msgs, uint64_t msg_count)
+{
+ uint64_t i;
+ bool result;
+
+ for (i = 0; i < msg_count; i++) {
+ if (!clock_classes_are_compatible_one(iterator, msgs[i])) {
+ result = false;
+ goto end;
+ }
+ }
+
+ result = true;
+
+end:
+ return result;
+}
+
+/*
+ * Call the `next` method of the iterator. Do some validation on the returned
+ * messages.
+ */
+
+static
+enum bt_component_class_message_iterator_next_method_status
+call_iterator_next_method(
+ struct bt_self_component_port_input_message_iterator *iterator,
+ bt_message_array_const msgs, uint64_t capacity, uint64_t *user_count)
+{
+ enum bt_component_class_message_iterator_next_method_status status;
+
+ BT_ASSERT_DBG(iterator->methods.next);
+ BT_LOGD_STR("Calling user's \"next\" method.");
+ status = iterator->methods.next(iterator, msgs, capacity, user_count);
+ BT_LOGD("User method returned: status=%s, msg-count=%" PRIu64,
+ bt_common_func_status_string(status), *user_count);
+
+ if (status == BT_FUNC_STATUS_OK) {
+ BT_ASSERT_POST_DEV(clock_classes_are_compatible(iterator, msgs, *user_count),
+ "Clocks are not compatible");
+ BT_ASSERT_POST_DEV(clock_snapshots_are_monotonic(iterator, msgs, *user_count),
+ "Clock snapshots are not monotonic");
+ }
+
+ return status;
+}
+
+enum bt_message_iterator_next_status